3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "jmutexautolock.h"
35 Map::Map(std::ostream &dout):
37 m_camera_position(0,0,0),
38 m_camera_direction(0,0,1),
42 m_sector_mutex.Init();
43 m_camera_mutex.Init();
44 assert(m_sector_mutex.IsInitialized());
45 assert(m_camera_mutex.IsInitialized());
47 // Get this so that the player can stay on it at first
48 //getSector(v2s16(0,0));
56 /*updater.setRun(false);
57 while(updater.IsRunning())
63 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
64 for(; i.atEnd() == false; i++)
66 MapSector *sector = i.getNode()->getValue();
71 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
73 if(m_sector_cache != NULL && p == m_sector_cache_p){
74 MapSector * sector = m_sector_cache;
75 // Reset inactivity timer
76 sector->usage_timer = 0.0;
80 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
85 MapSector *sector = n->getValue();
87 // Cache the last result
89 m_sector_cache = sector;
91 // Reset inactivity timer
92 sector->usage_timer = 0.0;
96 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
98 JMutexAutoLock lock(m_sector_mutex);
100 return getSectorNoGenerateNoExNoLock(p);
103 MapSector * Map::getSectorNoGenerate(v2s16 p)
105 MapSector *sector = getSectorNoGenerateNoEx(p);
107 throw InvalidPositionException();
112 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
114 v2s16 p2d(p3d.X, p3d.Z);
115 MapSector * sector = getSectorNoGenerate(p2d);
117 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
122 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
126 v2s16 p2d(p3d.X, p3d.Z);
127 MapSector * sector = getSectorNoGenerate(p2d);
128 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
131 catch(InvalidPositionException &e)
137 /*MapBlock * Map::getBlockCreate(v3s16 p3d)
139 v2s16 p2d(p3d.X, p3d.Z);
140 MapSector * sector = getSectorCreate(p2d);
142 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
145 block = sector->createBlankBlock(p3d.Y);
149 f32 Map::getGroundHeight(v2s16 p, bool generate)
152 v2s16 sectorpos = getNodeSectorPos(p);
153 MapSector * sref = getSectorNoGenerate(sectorpos);
154 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
155 f32 y = sref->getGroundHeight(relpos);
158 catch(InvalidPositionException &e)
160 return GROUNDHEIGHT_NOTFOUND_SETVALUE;
164 void Map::setGroundHeight(v2s16 p, f32 y, bool generate)
166 /*m_dout<<DTIME<<"Map::setGroundHeight(("
168 <<"), "<<y<<")"<<std::endl;*/
169 v2s16 sectorpos = getNodeSectorPos(p);
170 MapSector * sref = getSectorNoGenerate(sectorpos);
171 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
172 //sref->mutex.Lock();
173 sref->setGroundHeight(relpos, y);
174 //sref->mutex.Unlock();
177 bool Map::isNodeUnderground(v3s16 p)
179 v3s16 blockpos = getNodeBlockPos(p);
181 MapBlock * block = getBlockNoCreate(blockpos);
182 return block->getIsUnderground();
184 catch(InvalidPositionException &e)
191 Goes recursively through the neighbours of the node.
193 Alters only transparent nodes.
195 If the lighting of the neighbour is lower than the lighting of
196 the node was (before changing it to 0 at the step before), the
197 lighting of the neighbour is set to 0 and then the same stuff
198 repeats for the neighbour.
200 The ending nodes of the routine are stored in light_sources.
201 This is useful when a light is removed. In such case, this
202 routine can be called for the light node and then again for
203 light_sources to re-light the area without the removed light.
205 values of from_nodes are lighting values.
207 void Map::unspreadLight(enum LightBank bank,
208 core::map<v3s16, u8> & from_nodes,
209 core::map<v3s16, bool> & light_sources,
210 core::map<v3s16, MapBlock*> & modified_blocks)
213 v3s16(0,0,1), // back
215 v3s16(1,0,0), // right
216 v3s16(0,0,-1), // front
217 v3s16(0,-1,0), // bottom
218 v3s16(-1,0,0), // left
221 if(from_nodes.size() == 0)
224 u32 blockchangecount = 0;
226 core::map<v3s16, u8> unlighted_nodes;
227 core::map<v3s16, u8>::Iterator j;
228 j = from_nodes.getIterator();
231 Initialize block cache
234 MapBlock *block = NULL;
235 // Cache this a bit, too
236 bool block_checked_in_modified = false;
238 for(; j.atEnd() == false; j++)
240 v3s16 pos = j.getNode()->getKey();
241 v3s16 blockpos = getNodeBlockPos(pos);
243 // Only fetch a new block if the block position has changed
245 if(block == NULL || blockpos != blockpos_last){
246 block = getBlockNoCreate(blockpos);
247 blockpos_last = blockpos;
249 block_checked_in_modified = false;
253 catch(InvalidPositionException &e)
261 // Calculate relative position in block
262 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
264 // Get node straight from the block
265 MapNode n = block->getNode(relpos);
267 u8 oldlight = j.getNode()->getValue();
269 // Loop through 6 neighbors
270 for(u16 i=0; i<6; i++)
272 // Get the position of the neighbor node
273 v3s16 n2pos = pos + dirs[i];
275 // Get the block where the node is located
276 v3s16 blockpos = getNodeBlockPos(n2pos);
280 // Only fetch a new block if the block position has changed
282 if(block == NULL || blockpos != blockpos_last){
283 block = getBlockNoCreate(blockpos);
284 blockpos_last = blockpos;
286 block_checked_in_modified = false;
290 catch(InvalidPositionException &e)
295 // Calculate relative position in block
296 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
297 // Get node straight from the block
298 MapNode n2 = block->getNode(relpos);
300 bool changed = false;
302 //TODO: Optimize output by optimizing light_sources?
305 If the neighbor is dimmer than what was specified
306 as oldlight (the light of the previous node)
308 if(n2.getLight(bank) < oldlight)
311 And the neighbor is transparent and it has some light
313 if(n2.light_propagates() && n2.getLight(bank) != 0)
316 Set light to 0 and add to queue
319 u8 current_light = n2.getLight(bank);
320 n2.setLight(bank, 0);
321 block->setNode(relpos, n2);
323 unlighted_nodes.insert(n2pos, current_light);
327 Remove from light_sources if it is there
328 NOTE: This doesn't happen nearly at all
330 /*if(light_sources.find(n2pos))
332 std::cout<<"Removed from light_sources"<<std::endl;
333 light_sources.remove(n2pos);
338 if(light_sources.find(n2pos) != NULL)
339 light_sources.remove(n2pos);*/
342 light_sources.insert(n2pos, true);
345 // Add to modified_blocks
346 if(changed == true && block_checked_in_modified == false)
348 // If the block is not found in modified_blocks, add.
349 if(modified_blocks.find(blockpos) == NULL)
351 modified_blocks.insert(blockpos, block);
353 block_checked_in_modified = true;
356 catch(InvalidPositionException &e)
363 /*dstream<<"unspreadLight(): Changed block "
364 <<blockchangecount<<" times"
365 <<" for "<<from_nodes.size()<<" nodes"
368 if(unlighted_nodes.size() > 0)
369 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
373 A single-node wrapper of the above
375 void Map::unLightNeighbors(enum LightBank bank,
376 v3s16 pos, u8 lightwas,
377 core::map<v3s16, bool> & light_sources,
378 core::map<v3s16, MapBlock*> & modified_blocks)
380 core::map<v3s16, u8> from_nodes;
381 from_nodes.insert(pos, lightwas);
383 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
387 Lights neighbors of from_nodes, collects all them and then
390 void Map::spreadLight(enum LightBank bank,
391 core::map<v3s16, bool> & from_nodes,
392 core::map<v3s16, MapBlock*> & modified_blocks)
394 const v3s16 dirs[6] = {
395 v3s16(0,0,1), // back
397 v3s16(1,0,0), // right
398 v3s16(0,0,-1), // front
399 v3s16(0,-1,0), // bottom
400 v3s16(-1,0,0), // left
403 if(from_nodes.size() == 0)
406 u32 blockchangecount = 0;
408 core::map<v3s16, bool> lighted_nodes;
409 core::map<v3s16, bool>::Iterator j;
410 j = from_nodes.getIterator();
413 Initialize block cache
416 MapBlock *block = NULL;
417 // Cache this a bit, too
418 bool block_checked_in_modified = false;
420 for(; j.atEnd() == false; j++)
421 //for(; j != from_nodes.end(); j++)
423 v3s16 pos = j.getNode()->getKey();
425 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
426 v3s16 blockpos = getNodeBlockPos(pos);
428 // Only fetch a new block if the block position has changed
430 if(block == NULL || blockpos != blockpos_last){
431 block = getBlockNoCreate(blockpos);
432 blockpos_last = blockpos;
434 block_checked_in_modified = false;
438 catch(InvalidPositionException &e)
446 // Calculate relative position in block
447 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
449 // Get node straight from the block
450 MapNode n = block->getNode(relpos);
452 u8 oldlight = n.getLight(bank);
453 u8 newlight = diminish_light(oldlight);
455 // Loop through 6 neighbors
456 for(u16 i=0; i<6; i++){
457 // Get the position of the neighbor node
458 v3s16 n2pos = pos + dirs[i];
460 // Get the block where the node is located
461 v3s16 blockpos = getNodeBlockPos(n2pos);
465 // Only fetch a new block if the block position has changed
467 if(block == NULL || blockpos != blockpos_last){
468 block = getBlockNoCreate(blockpos);
469 blockpos_last = blockpos;
471 block_checked_in_modified = false;
475 catch(InvalidPositionException &e)
480 // Calculate relative position in block
481 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
482 // Get node straight from the block
483 MapNode n2 = block->getNode(relpos);
485 bool changed = false;
487 If the neighbor is brighter than the current node,
488 add to list (it will light up this node on its turn)
490 if(n2.getLight(bank) > undiminish_light(oldlight))
492 lighted_nodes.insert(n2pos, true);
493 //lighted_nodes.push_back(n2pos);
497 If the neighbor is dimmer than how much light this node
498 would spread on it, add to list
500 if(n2.getLight(bank) < newlight)
502 if(n2.light_propagates())
504 n2.setLight(bank, newlight);
505 block->setNode(relpos, n2);
506 lighted_nodes.insert(n2pos, true);
507 //lighted_nodes.push_back(n2pos);
512 // Add to modified_blocks
513 if(changed == true && block_checked_in_modified == false)
515 // If the block is not found in modified_blocks, add.
516 if(modified_blocks.find(blockpos) == NULL)
518 modified_blocks.insert(blockpos, block);
520 block_checked_in_modified = true;
523 catch(InvalidPositionException &e)
530 /*dstream<<"spreadLight(): Changed block "
531 <<blockchangecount<<" times"
532 <<" for "<<from_nodes.size()<<" nodes"
535 if(lighted_nodes.size() > 0)
536 spreadLight(bank, lighted_nodes, modified_blocks);
540 A single-node source variation of the above.
542 void Map::lightNeighbors(enum LightBank bank,
544 core::map<v3s16, MapBlock*> & modified_blocks)
546 core::map<v3s16, bool> from_nodes;
547 from_nodes.insert(pos, true);
548 spreadLight(bank, from_nodes, modified_blocks);
551 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
554 v3s16(0,0,1), // back
556 v3s16(1,0,0), // right
557 v3s16(0,0,-1), // front
558 v3s16(0,-1,0), // bottom
559 v3s16(-1,0,0), // left
562 u8 brightest_light = 0;
563 v3s16 brightest_pos(0,0,0);
564 bool found_something = false;
566 // Loop through 6 neighbors
567 for(u16 i=0; i<6; i++){
568 // Get the position of the neighbor node
569 v3s16 n2pos = p + dirs[i];
574 catch(InvalidPositionException &e)
578 if(n2.getLight(bank) > brightest_light || found_something == false){
579 brightest_light = n2.getLight(bank);
580 brightest_pos = n2pos;
581 found_something = true;
585 if(found_something == false)
586 throw InvalidPositionException();
588 return brightest_pos;
592 Propagates sunlight down from a node.
593 Starting point gets sunlight.
595 Returns the lowest y value of where the sunlight went.
597 Mud is turned into grass in where the sunlight stops.
599 s16 Map::propagateSunlight(v3s16 start,
600 core::map<v3s16, MapBlock*> & modified_blocks)
605 v3s16 pos(start.X, y, start.Z);
607 v3s16 blockpos = getNodeBlockPos(pos);
610 block = getBlockNoCreate(blockpos);
612 catch(InvalidPositionException &e)
617 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
618 MapNode n = block->getNode(relpos);
620 if(n.sunlight_propagates())
622 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
623 block->setNode(relpos, n);
625 modified_blocks.insert(blockpos, block);
629 // Turn mud into grass
630 if(n.d == CONTENT_MUD)
633 block->setNode(relpos, n);
634 modified_blocks.insert(blockpos, block);
637 // Sunlight goes no further
644 void Map::updateLighting(enum LightBank bank,
645 core::map<v3s16, MapBlock*> & a_blocks,
646 core::map<v3s16, MapBlock*> & modified_blocks)
648 /*m_dout<<DTIME<<"Map::updateLighting(): "
649 <<a_blocks.size()<<" blocks."<<std::endl;*/
651 //TimeTaker timer("updateLighting");
655 //u32 count_was = modified_blocks.size();
657 core::map<v3s16, MapBlock*> blocks_to_update;
659 core::map<v3s16, bool> light_sources;
661 core::map<v3s16, u8> unlight_from;
663 core::map<v3s16, MapBlock*>::Iterator i;
664 i = a_blocks.getIterator();
665 for(; i.atEnd() == false; i++)
667 MapBlock *block = i.getNode()->getValue();
671 // Don't bother with dummy blocks.
675 v3s16 pos = block->getPos();
676 modified_blocks.insert(pos, block);
678 blocks_to_update.insert(pos, block);
681 Clear all light from block
683 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
684 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
685 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
690 MapNode n = block->getNode(v3s16(x,y,z));
691 u8 oldlight = n.getLight(bank);
693 block->setNode(v3s16(x,y,z), n);
695 // Collect borders for unlighting
696 if(x==0 || x == MAP_BLOCKSIZE-1
697 || y==0 || y == MAP_BLOCKSIZE-1
698 || z==0 || z == MAP_BLOCKSIZE-1)
700 v3s16 p_map = p + v3s16(
703 MAP_BLOCKSIZE*pos.Z);
704 unlight_from.insert(p_map, oldlight);
707 catch(InvalidPositionException &e)
710 This would happen when dealing with a
714 dstream<<"updateLighting(): InvalidPositionException"
719 if(bank == LIGHTBANK_DAY)
721 bool bottom_valid = block->propagateSunlight(light_sources);
723 // If bottom is valid, we're done.
727 else if(bank == LIGHTBANK_NIGHT)
729 // For night lighting, sunlight is not propagated
734 // Invalid lighting bank
738 /*dstream<<"Bottom for sunlight-propagated block ("
739 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
742 // Bottom sunlight is not valid; get the block and loop to it
746 block = getBlockNoCreate(pos);
748 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;
770 TimeTaker timer("spreadLight");
771 spreadLight(bank, light_sources, modified_blocks);
776 u32 diff = modified_blocks.size() - count_was;
777 count_was = modified_blocks.size();
778 dstream<<"spreadLight modified "<<diff<<std::endl;
783 //MapVoxelManipulator vmanip(this);
785 // Make a manual voxel manipulator and load all the blocks
786 // that touch the requested blocks
787 ManualMapVoxelManipulator vmanip(this);
788 core::map<v3s16, MapBlock*>::Iterator i;
789 i = blocks_to_update.getIterator();
790 for(; i.atEnd() == false; i++)
792 MapBlock *block = i.getNode()->getValue();
793 v3s16 p = block->getPos();
795 // Add all surrounding blocks
796 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
799 Add all surrounding blocks that have up-to-date lighting
800 NOTE: This doesn't quite do the job (not everything
801 appropriate is lighted)
803 /*for(s16 z=-1; z<=1; z++)
804 for(s16 y=-1; y<=1; y++)
805 for(s16 x=-1; x<=1; x++)
808 MapBlock *block = getBlockNoCreateNoEx(p);
813 if(block->getLightingExpired())
815 vmanip.initialEmerge(p, p);
818 // Lighting of block will be updated completely
819 block->setLightingExpired(false);
823 //TimeTaker timer("unSpreadLight");
824 vmanip.unspreadLight(bank, unlight_from, light_sources);
827 //TimeTaker timer("spreadLight");
828 vmanip.spreadLight(bank, light_sources);
831 //TimeTaker timer("blitBack");
832 vmanip.blitBack(modified_blocks);
834 /*dstream<<"emerge_time="<<emerge_time<<std::endl;
838 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
841 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
842 core::map<v3s16, MapBlock*> & modified_blocks)
844 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
845 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
848 Update information about whether day and night light differ
850 for(core::map<v3s16, MapBlock*>::Iterator
851 i = modified_blocks.getIterator();
852 i.atEnd() == false; i++)
854 MapBlock *block = i.getNode()->getValue();
855 block->updateDayNightDiff();
860 This is called after changing a node from transparent to opaque.
861 The lighting value of the node should be left as-is after changing
862 other values. This sets the lighting value to 0.
864 /*void Map::nodeAddedUpdate(v3s16 p, u8 lightwas,
865 core::map<v3s16, MapBlock*> &modified_blocks)*/
866 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
867 core::map<v3s16, MapBlock*> &modified_blocks)
870 m_dout<<DTIME<<"Map::nodeAddedUpdate(): p=("
871 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
874 From this node to nodes underneath:
875 If lighting is sunlight (1.0), unlight neighbours and
880 v3s16 toppos = p + v3s16(0,1,0);
881 v3s16 bottompos = p + v3s16(0,-1,0);
883 bool node_under_sunlight = true;
884 core::map<v3s16, bool> light_sources;
887 If there is a node at top and it doesn't have sunlight,
888 there has not been any sunlight going down.
890 Otherwise there probably is.
893 MapNode topnode = getNode(toppos);
895 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
896 node_under_sunlight = false;
898 catch(InvalidPositionException &e)
902 if(n.d != CONTENT_TORCH)
905 If there is grass below, change it to mud
908 MapNode bottomnode = getNode(bottompos);
910 if(bottomnode.d == CONTENT_GRASS
911 || bottomnode.d == CONTENT_GRASS_FOOTSTEPS)
913 bottomnode.d = CONTENT_MUD;
914 setNode(bottompos, bottomnode);
917 catch(InvalidPositionException &e)
922 enum LightBank banks[] =
927 for(s32 i=0; i<2; i++)
929 enum LightBank bank = banks[i];
931 u8 lightwas = getNode(p).getLight(bank);
933 // Add the block of the added node to modified_blocks
934 v3s16 blockpos = getNodeBlockPos(p);
935 MapBlock * block = getBlockNoCreate(blockpos);
936 assert(block != NULL);
937 modified_blocks.insert(blockpos, block);
939 if(isValidPosition(p) == false)
942 // Unlight neighbours of node.
943 // This means setting light of all consequent dimmer nodes
945 // This also collects the nodes at the border which will spread
946 // light again into this.
947 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
955 If node is under sunlight, take all sunlighted nodes under
956 it and clear light from them and from where the light has
958 TODO: This could be optimized by mass-unlighting instead
961 if(node_under_sunlight)
965 //m_dout<<DTIME<<"y="<<y<<std::endl;
966 v3s16 n2pos(p.X, y, p.Z);
972 catch(InvalidPositionException &e)
977 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
979 //m_dout<<DTIME<<"doing"<<std::endl;
980 unLightNeighbors(LIGHTBANK_DAY,
981 n2pos, n2.getLight(LIGHTBANK_DAY),
982 light_sources, modified_blocks);
983 n2.setLight(LIGHTBANK_DAY, 0);
991 for(s32 i=0; i<2; i++)
993 enum LightBank bank = banks[i];
996 Spread light from all nodes that might be capable of doing so
997 TODO: Convert to spreadLight
999 spreadLight(bank, light_sources, modified_blocks);
1003 Update information about whether day and night light differ
1005 for(core::map<v3s16, MapBlock*>::Iterator
1006 i = modified_blocks.getIterator();
1007 i.atEnd() == false; i++)
1009 MapBlock *block = i.getNode()->getValue();
1010 block->updateDayNightDiff();
1014 Add neighboring liquid nodes and the node itself if it is
1015 liquid (=water node was added) to transform queue.
1018 v3s16(0,0,0), // self
1019 v3s16(0,0,1), // back
1020 v3s16(0,1,0), // top
1021 v3s16(1,0,0), // right
1022 v3s16(0,0,-1), // front
1023 v3s16(0,-1,0), // bottom
1024 v3s16(-1,0,0), // left
1026 for(u16 i=0; i<7; i++)
1031 v3s16 p2 = p + dirs[i];
1033 MapNode n2 = getNode(p2);
1034 if(content_liquid(n2.d))
1036 m_transforming_liquid.push_back(p2);
1039 }catch(InvalidPositionException &e)
1047 void Map::removeNodeAndUpdate(v3s16 p,
1048 core::map<v3s16, MapBlock*> &modified_blocks)
1050 /*PrintInfo(m_dout);
1051 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1052 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1054 bool node_under_sunlight = true;
1056 v3s16 toppos = p + v3s16(0,1,0);
1058 // Node will be replaced with this
1059 u8 replace_material = CONTENT_AIR;
1062 If there is a node at top and it doesn't have sunlight,
1063 there will be no sunlight going down.
1066 MapNode topnode = getNode(toppos);
1068 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1069 node_under_sunlight = false;
1071 catch(InvalidPositionException &e)
1075 core::map<v3s16, bool> light_sources;
1077 enum LightBank banks[] =
1082 for(s32 i=0; i<2; i++)
1084 enum LightBank bank = banks[i];
1087 Unlight neighbors (in case the node is a light source)
1089 unLightNeighbors(bank, p,
1090 getNode(p).getLight(bank),
1091 light_sources, modified_blocks);
1096 This also clears the lighting.
1100 n.d = replace_material;
1103 for(s32 i=0; i<2; i++)
1105 enum LightBank bank = banks[i];
1108 Recalculate lighting
1110 spreadLight(bank, light_sources, modified_blocks);
1113 // Add the block of the removed node to modified_blocks
1114 v3s16 blockpos = getNodeBlockPos(p);
1115 MapBlock * block = getBlockNoCreate(blockpos);
1116 assert(block != NULL);
1117 modified_blocks.insert(blockpos, block);
1120 If the removed node was under sunlight, propagate the
1121 sunlight down from it and then light all neighbors
1122 of the propagated blocks.
1124 if(node_under_sunlight)
1126 s16 ybottom = propagateSunlight(p, modified_blocks);
1127 /*m_dout<<DTIME<<"Node was under sunlight. "
1128 "Propagating sunlight";
1129 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1131 for(; y >= ybottom; y--)
1133 v3s16 p2(p.X, y, p.Z);
1134 /*m_dout<<DTIME<<"lighting neighbors of node ("
1135 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1137 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1142 // Set the lighting of this node to 0
1143 // TODO: Is this needed? Lighting is cleared up there already.
1145 MapNode n = getNode(p);
1146 n.setLight(LIGHTBANK_DAY, 0);
1149 catch(InvalidPositionException &e)
1155 for(s32 i=0; i<2; i++)
1157 enum LightBank bank = banks[i];
1159 // Get the brightest neighbour node and propagate light from it
1160 v3s16 n2p = getBrightestNeighbour(bank, p);
1162 MapNode n2 = getNode(n2p);
1163 lightNeighbors(bank, n2p, modified_blocks);
1165 catch(InvalidPositionException &e)
1171 Update information about whether day and night light differ
1173 for(core::map<v3s16, MapBlock*>::Iterator
1174 i = modified_blocks.getIterator();
1175 i.atEnd() == false; i++)
1177 MapBlock *block = i.getNode()->getValue();
1178 block->updateDayNightDiff();
1182 Add neighboring liquid nodes to transform queue.
1185 v3s16(0,0,1), // back
1186 v3s16(0,1,0), // top
1187 v3s16(1,0,0), // right
1188 v3s16(0,0,-1), // front
1189 v3s16(0,-1,0), // bottom
1190 v3s16(-1,0,0), // left
1192 for(u16 i=0; i<6; i++)
1197 v3s16 p2 = p + dirs[i];
1199 MapNode n2 = getNode(p2);
1200 if(content_liquid(n2.d))
1202 m_transforming_liquid.push_back(p2);
1205 }catch(InvalidPositionException &e)
1212 void Map::expireMeshes(bool only_daynight_diffed)
1214 TimeTaker timer("expireMeshes()");
1216 core::map<v2s16, MapSector*>::Iterator si;
1217 si = m_sectors.getIterator();
1218 for(; si.atEnd() == false; si++)
1220 MapSector *sector = si.getNode()->getValue();
1222 core::list< MapBlock * > sectorblocks;
1223 sector->getBlocks(sectorblocks);
1225 core::list< MapBlock * >::Iterator i;
1226 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1228 MapBlock *block = *i;
1230 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
1236 JMutexAutoLock lock(block->mesh_mutex);
1237 if(block->mesh != NULL)
1239 /*block->mesh->drop();
1240 block->mesh = NULL;*/
1241 block->setMeshExpired(true);
1248 void Map::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
1250 assert(mapType() == MAPTYPE_CLIENT);
1253 v3s16 p = blockpos + v3s16(0,0,0);
1254 MapBlock *b = getBlockNoCreate(p);
1255 b->updateMesh(daynight_ratio);
1257 catch(InvalidPositionException &e){}
1260 v3s16 p = blockpos + v3s16(-1,0,0);
1261 MapBlock *b = getBlockNoCreate(p);
1262 b->updateMesh(daynight_ratio);
1264 catch(InvalidPositionException &e){}
1266 v3s16 p = blockpos + v3s16(0,-1,0);
1267 MapBlock *b = getBlockNoCreate(p);
1268 b->updateMesh(daynight_ratio);
1270 catch(InvalidPositionException &e){}
1272 v3s16 p = blockpos + v3s16(0,0,-1);
1273 MapBlock *b = getBlockNoCreate(p);
1274 b->updateMesh(daynight_ratio);
1276 catch(InvalidPositionException &e){}
1279 v3s16 p = blockpos + v3s16(1,0,0);
1280 MapBlock *b = getBlockNoCreate(p);
1281 b->updateMesh(daynight_ratio);
1283 catch(InvalidPositionException &e){}
1285 v3s16 p = blockpos + v3s16(0,1,0);
1286 MapBlock *b = getBlockNoCreate(p);
1287 b->updateMesh(daynight_ratio);
1289 catch(InvalidPositionException &e){}
1291 v3s16 p = blockpos + v3s16(0,0,1);
1292 MapBlock *b = getBlockNoCreate(p);
1293 b->updateMesh(daynight_ratio);
1295 catch(InvalidPositionException &e){}*/
1300 bool Map::dayNightDiffed(v3s16 blockpos)
1303 v3s16 p = blockpos + v3s16(0,0,0);
1304 MapBlock *b = getBlockNoCreate(p);
1305 if(b->dayNightDiffed())
1308 catch(InvalidPositionException &e){}
1311 v3s16 p = blockpos + v3s16(-1,0,0);
1312 MapBlock *b = getBlockNoCreate(p);
1313 if(b->dayNightDiffed())
1316 catch(InvalidPositionException &e){}
1318 v3s16 p = blockpos + v3s16(0,-1,0);
1319 MapBlock *b = getBlockNoCreate(p);
1320 if(b->dayNightDiffed())
1323 catch(InvalidPositionException &e){}
1325 v3s16 p = blockpos + v3s16(0,0,-1);
1326 MapBlock *b = getBlockNoCreate(p);
1327 if(b->dayNightDiffed())
1330 catch(InvalidPositionException &e){}
1333 v3s16 p = blockpos + v3s16(1,0,0);
1334 MapBlock *b = getBlockNoCreate(p);
1335 if(b->dayNightDiffed())
1338 catch(InvalidPositionException &e){}
1340 v3s16 p = blockpos + v3s16(0,1,0);
1341 MapBlock *b = getBlockNoCreate(p);
1342 if(b->dayNightDiffed())
1345 catch(InvalidPositionException &e){}
1347 v3s16 p = blockpos + v3s16(0,0,1);
1348 MapBlock *b = getBlockNoCreate(p);
1349 if(b->dayNightDiffed())
1352 catch(InvalidPositionException &e){}
1358 Updates usage timers
1360 void Map::timerUpdate(float dtime)
1362 JMutexAutoLock lock(m_sector_mutex);
1364 core::map<v2s16, MapSector*>::Iterator si;
1366 si = m_sectors.getIterator();
1367 for(; si.atEnd() == false; si++)
1369 MapSector *sector = si.getNode()->getValue();
1370 sector->usage_timer += dtime;
1374 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1377 Wait for caches to be removed before continuing.
1379 This disables the existence of caches while locked
1381 //SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
1383 core::list<v2s16>::Iterator j;
1384 for(j=list.begin(); j!=list.end(); j++)
1386 MapSector *sector = m_sectors[*j];
1389 sector->deleteBlocks();
1394 If sector is in sector cache, remove it from there
1396 if(m_sector_cache == sector)
1398 m_sector_cache = NULL;
1401 Remove from map and delete
1403 m_sectors.remove(*j);
1409 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1410 core::list<v3s16> *deleted_blocks)
1412 JMutexAutoLock lock(m_sector_mutex);
1414 core::list<v2s16> sector_deletion_queue;
1415 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1416 for(; i.atEnd() == false; i++)
1418 MapSector *sector = i.getNode()->getValue();
1420 Delete sector from memory if it hasn't been used in a long time
1422 if(sector->usage_timer > timeout)
1424 sector_deletion_queue.push_back(i.getNode()->getKey());
1426 if(deleted_blocks != NULL)
1428 // Collect positions of blocks of sector
1429 MapSector *sector = i.getNode()->getValue();
1430 core::list<MapBlock*> blocks;
1431 sector->getBlocks(blocks);
1432 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1433 i != blocks.end(); i++)
1435 deleted_blocks->push_back((*i)->getPos());
1440 deleteSectors(sector_deletion_queue, only_blocks);
1441 return sector_deletion_queue.getSize();
1444 void Map::PrintInfo(std::ostream &out)
1449 #define WATER_DROP_BOOST 4
1451 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1453 DSTACK(__FUNCTION_NAME);
1454 //TimeTaker timer("transformLiquids()");
1457 u32 initial_size = m_transforming_liquid.size();
1459 if(initial_size != 0)
1460 dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;
1462 while(m_transforming_liquid.size() != 0)
1465 Get a queued transforming liquid node
1467 v3s16 p0 = m_transforming_liquid.pop_front();
1469 MapNode n0 = getNode(p0);
1471 // Don't deal with non-liquids
1472 if(content_liquid(n0.d) == false)
1475 bool is_source = !content_flowing_liquid(n0.d);
1477 u8 liquid_level = 8;
1478 if(is_source == false)
1479 liquid_level = n0.param2 & 0x0f;
1481 // Turn possible source into non-source
1482 u8 nonsource_c = make_liquid_flowing(n0.d);
1485 If not source, check that some node flows into this one
1486 and what is the level of liquid in this one
1488 if(is_source == false)
1490 s8 new_liquid_level_max = -1;
1492 v3s16 dirs_from[5] = {
1493 v3s16(0,1,0), // top
1494 v3s16(0,0,1), // back
1495 v3s16(1,0,0), // right
1496 v3s16(0,0,-1), // front
1497 v3s16(-1,0,0), // left
1499 for(u16 i=0; i<5; i++)
1504 bool from_top = (i==0);
1506 v3s16 p2 = p0 + dirs_from[i];
1507 MapNode n2 = getNode(p2);
1509 if(content_liquid(n2.d))
1511 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1512 // Check that the liquids are the same type
1513 if(n2_nonsource_c != nonsource_c)
1515 dstream<<"WARNING: Not handling: different liquids"
1516 " collide"<<std::endl;
1519 bool n2_is_source = !content_flowing_liquid(n2.d);
1520 s8 n2_liquid_level = 8;
1521 if(n2_is_source == false)
1522 n2_liquid_level = n2.param2 & 0x07;
1524 s8 new_liquid_level = -1;
1527 //new_liquid_level = 7;
1528 if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
1529 new_liquid_level = 7;
1531 new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
1533 else if(n2_liquid_level > 0)
1535 new_liquid_level = n2_liquid_level - 1;
1538 if(new_liquid_level > new_liquid_level_max)
1539 new_liquid_level_max = new_liquid_level;
1542 }catch(InvalidPositionException &e)
1548 If liquid level should be something else, update it and
1549 add all the neighboring water nodes to the transform queue.
1551 if(new_liquid_level_max != liquid_level)
1553 if(new_liquid_level_max == -1)
1555 // Remove water alltoghether
1562 n0.param2 = new_liquid_level_max;
1566 // Block has been modified
1568 v3s16 blockpos = getNodeBlockPos(p0);
1569 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1571 modified_blocks.insert(blockpos, block);
1575 Add neighboring non-source liquid nodes to transform queue.
1578 v3s16(0,0,1), // back
1579 v3s16(0,1,0), // top
1580 v3s16(1,0,0), // right
1581 v3s16(0,0,-1), // front
1582 v3s16(0,-1,0), // bottom
1583 v3s16(-1,0,0), // left
1585 for(u16 i=0; i<6; i++)
1590 v3s16 p2 = p0 + dirs[i];
1592 MapNode n2 = getNode(p2);
1593 if(content_flowing_liquid(n2.d))
1595 m_transforming_liquid.push_back(p2);
1598 }catch(InvalidPositionException &e)
1605 // Get a new one from queue if the node has turned into non-water
1606 if(content_liquid(n0.d) == false)
1610 Flow water from this node
1612 v3s16 dirs_to[5] = {
1613 v3s16(0,-1,0), // bottom
1614 v3s16(0,0,1), // back
1615 v3s16(1,0,0), // right
1616 v3s16(0,0,-1), // front
1617 v3s16(-1,0,0), // left
1619 for(u16 i=0; i<5; i++)
1624 bool to_bottom = (i == 0);
1626 // If liquid is at lowest possible height, it's not going
1627 // anywhere except down
1628 if(liquid_level == 0 && to_bottom == false)
1631 u8 liquid_next_level = 0;
1632 // If going to bottom
1635 //liquid_next_level = 7;
1636 if(liquid_level >= 7 - WATER_DROP_BOOST)
1637 liquid_next_level = 7;
1639 liquid_next_level = liquid_level + WATER_DROP_BOOST;
1642 liquid_next_level = liquid_level - 1;
1644 bool n2_changed = false;
1645 bool flowed = false;
1647 v3s16 p2 = p0 + dirs_to[i];
1649 MapNode n2 = getNode(p2);
1650 //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
1652 if(content_liquid(n2.d))
1654 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1655 // Check that the liquids are the same type
1656 if(n2_nonsource_c != nonsource_c)
1658 dstream<<"WARNING: Not handling: different liquids"
1659 " collide"<<std::endl;
1662 bool n2_is_source = !content_flowing_liquid(n2.d);
1663 u8 n2_liquid_level = 8;
1664 if(n2_is_source == false)
1665 n2_liquid_level = n2.param2 & 0x07;
1674 // Just flow into the source, nothing changes.
1675 // n2_changed is not set because destination didn't change
1680 if(liquid_next_level > liquid_level)
1682 n2.param2 = liquid_next_level;
1690 else if(n2.d == CONTENT_AIR)
1693 n2.param2 = liquid_next_level;
1700 //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
1704 m_transforming_liquid.push_back(p2);
1706 v3s16 blockpos = getNodeBlockPos(p2);
1707 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1709 modified_blocks.insert(blockpos, block);
1712 // If n2_changed to bottom, don't flow anywhere else
1713 if(to_bottom && flowed && !is_source)
1716 }catch(InvalidPositionException &e)
1722 //if(loopcount >= 100000)
1723 if(loopcount >= initial_size * 1)
1726 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1733 ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
1745 // TODO: Save to and load from a file
1746 m_seed = (((u64)myrand()<<0)%0x7fff)
1747 + (((u64)myrand()<<16)%0x7fff)
1748 + (((u64)myrand()<<32)%0x7fff)
1749 + (((u64)myrand()<<48)%0x7fff);
1752 Experimental and debug stuff
1756 dstream<<"Generating map point attribute lists"<<std::endl;
1758 PointAttributeList *list_baseheight = m_padb.getList("hm_baseheight");
1759 PointAttributeList *list_randmax = m_padb.getList("hm_randmax");
1760 PointAttributeList *list_randfactor = m_padb.getList("hm_randfactor");
1761 //PointAttributeList *list_plants_amount = m_padb.getList("plants_amount");
1762 //PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
1766 NOTE: BEWARE: Too big amount of these will make map generation
1767 slow. Especially those that are read by every block emerge.
1775 for(u32 i=0; i<500; i++)
1777 /*u32 lim = MAP_GENERATION_LIMIT;
1781 u32 lim = 500 + MAP_GENERATION_LIMIT * i / 500;
1784 -lim + myrand()%(lim*2),
1786 -lim + myrand()%(lim*2)
1788 /*float plants_amount = (float)(myrand()%1050) / 1000.0;
1789 plants_amount = pow(plants_amount, 5);
1790 list_plants_amount->addPoint(p, Attribute(plants_amount));*/
1792 float plants_amount = 0;
1795 plants_amount = 1.5;
1797 else if(myrand()%4 == 0)
1799 plants_amount = 0.5;
1801 else if(myrand()%2 == 0)
1803 plants_amount = 0.03;
1807 plants_amount = 0.0;
1811 list_plants_amount->addPoint(p, Attribute(plants_amount));
1814 for(u32 i=0; i<500; i++)
1816 /*u32 lim = MAP_GENERATION_LIMIT;
1820 u32 lim = 500 + MAP_GENERATION_LIMIT * i / 500;
1823 -lim + myrand()%(lim*2),
1825 -lim + myrand()%(lim*2)
1828 float caves_amount = 0;
1833 else if(myrand()%3 == 0)
1839 caves_amount = 0.05;
1842 list_caves_amount->addPoint(p, Attribute(caves_amount));
1845 for(u32 i=0; i<500; i++)
1847 /*u32 lim = MAP_GENERATION_LIMIT;
1851 u32 lim = 500 + MAP_GENERATION_LIMIT * i / 500;
1854 -lim + (myrand()%(lim*2)),
1856 -lim + (myrand()%(lim*2))
1859 /*s32 bh_i = (myrand()%200) - 50;
1860 float baseheight = (float)bh_i;
1864 float randmax = (float)(myrand()%(int)(10.*pow(m, 1./e)))/10.;
1865 randmax = pow(randmax, e);
1867 //float randmax = (float)(myrand()%60);
1868 float randfactor = (float)(myrand()%450) / 1000.0 + 0.4;*/
1870 float baseheight = 0;
1872 float randfactor = 0;
1874 /*if(myrand()%5 == 0)
1880 else if(myrand()%6 == 0)
1886 else if(myrand()%4 == 0)
1892 else if(myrand()%3 == 0)
1918 list_baseheight->addPoint(p, Attribute(baseheight));
1919 list_randmax->addPoint(p, Attribute(randmax));
1920 list_randfactor->addPoint(p, Attribute(randfactor));
1924 // Add only one entry
1925 list_baseheight->addPoint(v3s16(0,0,0), Attribute(-4));
1926 list_randmax->addPoint(v3s16(0,0,0), Attribute(22));
1927 //list_randmax->addPoint(v3s16(0,0,0), Attribute(0));
1928 list_randfactor->addPoint(v3s16(0,0,0), Attribute(0.45));
1931 /*list_baseheight->addPoint(v3s16(0,0,0), Attribute(0));
1932 list_randmax->addPoint(v3s16(0,0,0), Attribute(10));
1933 list_randfactor->addPoint(v3s16(0,0,0), Attribute(0.65));*/
1937 Try to load map; if not found, create a new one.
1940 m_savedir = savedir;
1941 m_map_saving_enabled = false;
1945 // If directory exists, check contents and load if possible
1946 if(fs::PathExists(m_savedir))
1948 // If directory is empty, it is safe to save into it.
1949 if(fs::GetDirListing(m_savedir).size() == 0)
1951 dstream<<DTIME<<"Server: Empty save directory is valid."
1953 m_map_saving_enabled = true;
1957 // Load master heightmap
1958 loadMasterHeightmap();
1960 // Load sector (0,0) and throw and exception on fail
1961 if(loadSectorFull(v2s16(0,0)) == false)
1962 throw LoadError("Failed to load sector (0,0)");
1964 dstream<<DTIME<<"Server: Successfully loaded master "
1965 "heightmap and sector (0,0) from "<<savedir<<
1966 ", assuming valid save directory."
1969 m_map_saving_enabled = true;
1970 // Map loaded, not creating new one
1974 // If directory doesn't exist, it is safe to save to it
1976 m_map_saving_enabled = true;
1979 catch(std::exception &e)
1981 dstream<<DTIME<<"Server: Failed to load map from "<<savedir
1982 <<", exception: "<<e.what()<<std::endl;
1983 dstream<<DTIME<<"Please remove the map or fix it."<<std::endl;
1984 dstream<<DTIME<<"WARNING: Map saving will be disabled."<<std::endl;
1987 dstream<<DTIME<<"Initializing new map."<<std::endl;
1989 // Create master heightmap
1990 // NOTE: Yes, that is a magic number down there. It specifies
1991 // the size of the internal FixedHeightmaps.
1992 m_heightmap = new UnlimitedHeightmap
1995 // Set map parameters
1998 // Create zero sector
1999 emergeSector(v2s16(0,0));
2001 // Initially write whole map
2005 ServerMap::~ServerMap()
2009 if(m_map_saving_enabled)
2012 // Save only changed parts
2014 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
2018 dstream<<DTIME<<"Server: map not saved"<<std::endl;
2021 catch(std::exception &e)
2023 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
2024 <<", exception: "<<e.what()<<std::endl;
2027 if(m_heightmap != NULL)
2033 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2034 for(; i.atEnd() == false; i++)
2036 MapChunk *chunk = i.getNode()->getValue();
2042 Some helper functions for the map generator
2045 s16 find_ground_level(VoxelManipulator &vmanip, v2s16 p2d)
2047 v3s16 em = vmanip.m_area.getExtent();
2048 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
2049 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
2050 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2052 for(y=y_nodes_max; y>=y_nodes_min; y--)
2054 MapNode &n = vmanip.m_data[i];
2055 if(content_walkable(n.d))
2058 vmanip.m_area.add_y(em, i, -1);
2060 if(y >= y_nodes_min)
2066 s16 find_ground_level_clever(VoxelManipulator &vmanip, v2s16 p2d)
2068 v3s16 em = vmanip.m_area.getExtent();
2069 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
2070 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
2071 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2073 for(y=y_nodes_max; y>=y_nodes_min; y--)
2075 MapNode &n = vmanip.m_data[i];
2076 if(content_walkable(n.d)
2077 && n.d != CONTENT_TREE
2078 && n.d != CONTENT_LEAVES)
2081 vmanip.m_area.add_y(em, i, -1);
2083 if(y >= y_nodes_min)
2089 void make_tree(VoxelManipulator &vmanip, v3s16 p0)
2091 MapNode treenode(CONTENT_TREE);
2092 MapNode leavesnode(CONTENT_LEAVES);
2094 s16 trunk_h = myrand_range(3, 6);
2096 for(s16 ii=0; ii<trunk_h; ii++)
2098 if(vmanip.m_area.contains(p1))
2099 vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
2103 // p1 is now the last piece of the trunk
2106 VoxelArea leaves_a(v3s16(-2,-2,-2), v3s16(2,2,2));
2107 SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
2108 for(s32 i=0; i<leaves_a.getVolume(); i++)
2111 // Force leaves at near the end of the trunk
2114 for(s16 z=-d; z<=d; z++)
2115 for(s16 y=-d; y<=d; y++)
2116 for(s16 x=-d; x<=d; x++)
2118 leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
2122 // Add leaves randomly
2123 for(u32 iii=0; iii<7; iii++)
2128 myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
2129 myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
2130 myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
2133 for(s16 z=0; z<=d; z++)
2134 for(s16 y=0; y<=d; y++)
2135 for(s16 x=0; x<=d; x++)
2137 leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
2141 // Blit leaves to vmanip
2142 for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
2143 for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
2144 for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
2148 if(vmanip.m_area.contains(p) == false)
2150 u32 vi = vmanip.m_area.index(p);
2151 if(vmanip.m_data[vi].d != CONTENT_AIR)
2153 u32 i = leaves_a.index(x,y,z);
2154 if(leaves_d[i] == 1)
2155 vmanip.m_data[vi] = leavesnode;
2160 Noise functions. Make sure seed is mangled differently in each one.
2163 // Amount of trees per area in nodes
2164 double tree_amount_2d(u64 seed, v2s16 p)
2166 double noise = noise2d_perlin(
2167 0.5+(float)p.X/500, 0.5+(float)p.Y/500,
2169 double zeroval = -0.3;
2173 return 0.05 * (noise-zeroval) / (0.5-zeroval);
2176 double base_rock_level_2d(u64 seed, v2s16 p)
2178 return -4. + 25. * noise2d_perlin(
2179 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2183 double highlands_level_2d(u64 seed, v2s16 p)
2185 double a = noise2d_perlin(
2186 0.5+(float)p.X/1000., 0.5+(float)p.Y/1000.,
2190 return 35. + 10. * noise2d_perlin(
2191 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2192 seed+85039, 6, 0.55);
2199 This is the main map generation method
2202 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
2203 core::map<v3s16, MapBlock*> &changed_blocks)
2206 Don't generate if already fully generated
2209 MapChunk *chunk = getChunk(chunkpos);
2210 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
2212 dstream<<"generateChunkRaw(): Chunk "
2213 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2214 <<" already generated"<<std::endl;
2219 dstream<<"generateChunkRaw(): Generating chunk "
2220 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2223 TimeTaker timer("generateChunkRaw()");
2225 // The distance how far into the neighbors the generator is allowed to go.
2226 s16 max_spread_amount_sectors = 2;
2227 assert(max_spread_amount_sectors <= m_chunksize);
2228 s16 max_spread_amount = max_spread_amount_sectors * MAP_BLOCKSIZE;
2229 // Minimum amount of space left on sides for mud to fall in
2230 s16 min_mud_fall_space = 2;
2231 // Maximum diameter of stone obstacles in X and Z
2232 s16 stone_obstacle_max_size = (max_spread_amount-min_mud_fall_space)*2;
2233 assert(stone_obstacle_max_size/2 <= max_spread_amount-min_mud_fall_space);
2235 s16 y_blocks_min = -4;
2236 s16 y_blocks_max = 3;
2237 s16 h_blocks = y_blocks_max - y_blocks_min + 1;
2238 s16 y_nodes_min = y_blocks_min * MAP_BLOCKSIZE;
2239 s16 y_nodes_max = y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
2241 v2s16 sectorpos_base = chunk_to_sector(chunkpos);
2242 s16 sectorpos_base_size = m_chunksize;
2244 /*v2s16 sectorpos_bigbase = chunk_to_sector(chunkpos - v2s16(1,1));
2245 s16 sectorpos_bigbase_size = m_chunksize * 3;*/
2246 v2s16 sectorpos_bigbase =
2247 sectorpos_base - v2s16(1,1) * max_spread_amount_sectors;
2248 s16 sectorpos_bigbase_size =
2249 sectorpos_base_size + 2 * max_spread_amount_sectors;
2251 v3s16 bigarea_blocks_min(
2252 sectorpos_bigbase.X,
2257 v3s16 bigarea_blocks_max(
2258 sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
2260 sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
2263 // Relative values to control amount of stuff in one chunk
2264 /*u32 relative_area = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2265 *(u32)sectorpos_base_size*MAP_BLOCKSIZE;*/
2266 u32 relative_volume = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2267 *(u32)sectorpos_base_size*MAP_BLOCKSIZE
2268 *(u32)h_blocks*MAP_BLOCKSIZE;
2271 The limiting edges of the lighting update, inclusive.
2273 s16 lighting_min_d = 0-max_spread_amount;
2274 s16 lighting_max_d = sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2277 Create the whole area of this and the neighboring chunks
2280 TimeTaker timer("generateChunkRaw() create area");
2282 for(s16 x=0; x<sectorpos_bigbase_size; x++)
2283 for(s16 z=0; z<sectorpos_bigbase_size; z++)
2285 v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
2286 ServerMapSector *sector = createSector(sectorpos);
2289 for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
2291 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
2292 MapBlock *block = createBlock(blockpos);
2294 // Lighting won't be calculated
2295 //block->setLightingExpired(true);
2296 // Lighting will be calculated
2297 block->setLightingExpired(false);
2300 Block gets sunlight if this is true.
2302 This should be set to true when the top side of a block
2303 is completely exposed to the sky.
2305 Actually this doesn't matter now because the
2306 initial lighting is done here.
2308 block->setIsUnderground(y != y_blocks_max);
2314 Now we have a big empty area.
2316 Make a ManualMapVoxelManipulator that contains this and the
2320 ManualMapVoxelManipulator vmanip(this);
2321 // Add the area we just generated
2323 TimeTaker timer("generateChunkRaw() initialEmerge");
2324 vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2327 TimeTaker timer_generate("generateChunkRaw() generate");
2330 Generate general ground level to full area
2335 //TimeTaker timer1("ground level");
2337 for(s16 x=0; x<sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2338 for(s16 z=0; z<sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2341 v2s16 p2d = sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2344 Skip of already generated
2347 v3s16 p(p2d.X, y_nodes_min, p2d.Y);
2348 if(vmanip.m_data[vmanip.m_area.index(p)].d != CONTENT_AIR)
2352 // Ground height at this point
2353 float surface_y_f = 0.0;
2355 // Use perlin noise for ground height
2356 surface_y_f = base_rock_level_2d(m_seed, p2d);
2358 // Experimental stuff
2360 float a = highlands_level_2d(m_seed, p2d);
2365 // Convert to integer
2366 s16 surface_y = (s16)surface_y_f;
2369 Fill ground with stone
2372 // Use fast index incrementing
2373 v3s16 em = vmanip.m_area.getExtent();
2374 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2375 for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++)
2377 vmanip.m_data[i].d = CONTENT_STONE;
2379 vmanip.m_area.add_y(em, i, 1);
2387 Randomize some parameters
2390 u32 stone_obstacle_amount = 0;
2391 if(myrand() % 2 == 0)
2392 stone_obstacle_amount = myrand_range(0, myrand_range(20, 150));
2394 stone_obstacle_amount = myrand_range(0, myrand_range(20, 50));
2396 //u32 stone_obstacle_amount =
2397 // myrand_range(0, myrand_range(20, myrand_range(80,150)));
2400 Loop this part, it will make stuff look older and newer nicely
2403 for(u32 i_age=0; i_age<2; i_age++)
2406 // This is set during the next operation.
2407 // Maximum height of the stone surface and obstacles.
2408 // This is used to disable dungeon generation from going too high.
2409 s16 stone_surface_max_y = 0;
2413 //TimeTaker timer1("stone obstacles");
2416 Add some random stone obstacles
2419 for(u32 ri=0; ri<stone_obstacle_amount/3; ri++)
2420 //for(u32 ri=0; ri<7; ri++)
2423 // Randomize max height so usually stuff will be quite low
2424 //s16 maxheight_randomized = myrand_range(0, 25);
2425 s16 maxheight_randomized = myrand_range(0, stone_obstacle_amount/3);
2427 // The size of these could actually be m_chunksize*MAP_BLOCKSIZE*2
2429 myrand_range(5, stone_obstacle_max_size),
2430 myrand_range(0, maxheight_randomized),
2431 myrand_range(5, stone_obstacle_max_size)
2434 // Don't make stupid small rectable bumps
2439 myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1),
2440 myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1)
2443 Limit by 1 to not obstruct sunlight at borders, because
2444 it would fuck up lighting in some places because we're
2445 leaving out removing light from the borders for optimization
2449 myrand_range(1, sectorpos_base_size*MAP_BLOCKSIZE-1-1),
2450 myrand_range(1, sectorpos_base_size*MAP_BLOCKSIZE-1-1)
2453 // Minimum space left on top of the obstacle
2454 s16 min_head_space = 12;
2456 for(s16 x=-ob_size.X/2; x<ob_size.X/2; x++)
2457 for(s16 z=-ob_size.Z/2; z<ob_size.Z/2; z++)
2459 // Node position in 2d
2460 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + ob_place + v2s16(x,z);
2462 // Find stone ground level
2463 // (ignore everything else than mud in already generated chunks)
2464 // and mud amount over the stone level
2468 v3s16 em = vmanip.m_area.getExtent();
2469 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2471 // Go to ground level
2472 for(y=y_nodes_max; y>=y_nodes_min; y--)
2474 MapNode *n = &vmanip.m_data[i];
2475 /*if(content_walkable(n.d)
2476 && n.d != CONTENT_MUD
2477 && n.d != CONTENT_GRASS)
2479 if(n->d == CONTENT_STONE)
2482 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2486 Change to mud because otherwise we might
2487 be throwing mud on grass at the next
2493 vmanip.m_area.add_y(em, i, -1);
2495 if(y >= y_nodes_min)
2498 surface_y = y_nodes_min;
2506 v3s16 em = vmanip.m_area.getExtent();
2507 s16 y_start = surface_y+1;
2508 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2512 for(y=y_start; y<=y_nodes_max - min_head_space; y++)
2514 MapNode &n = vmanip.m_data[i];
2515 n.d = CONTENT_STONE;
2517 if(y > stone_surface_max_y)
2518 stone_surface_max_y = y;
2521 if(count >= ob_size.Y)
2524 vmanip.m_area.add_y(em, i, 1);
2528 for(; y<=y_nodes_max - min_head_space; y++)
2530 MapNode &n = vmanip.m_data[i];
2533 if(count >= mud_amount)
2536 vmanip.m_area.add_y(em, i, 1);
2546 //TimeTaker timer1("dungeons");
2551 u32 dungeons_count = relative_volume / 400000;
2552 u32 bruises_count = relative_volume * stone_surface_max_y / 200000 / 50;
2553 /*u32 dungeons_count = 0;
2554 u32 bruises_count = 0;*/
2555 for(u32 jj=0; jj<dungeons_count+bruises_count; jj++)
2557 s16 min_tunnel_diameter = 2;
2558 s16 max_tunnel_diameter = 6;
2559 u16 tunnel_routepoints = 15;
2561 bool bruise_surface = (jj < bruises_count);
2565 min_tunnel_diameter = 5;
2566 max_tunnel_diameter = myrand_range(10, 20);
2567 tunnel_routepoints = 3;
2570 // Allowed route area size in nodes
2572 sectorpos_base_size*MAP_BLOCKSIZE,
2573 h_blocks*MAP_BLOCKSIZE,
2574 sectorpos_base_size*MAP_BLOCKSIZE
2577 // Area starting point in nodes
2579 sectorpos_base.X*MAP_BLOCKSIZE,
2580 y_blocks_min*MAP_BLOCKSIZE,
2581 sectorpos_base.Y*MAP_BLOCKSIZE
2585 //(this should be more than the maximum radius of the tunnel)
2586 //s16 insure = 5; // Didn't work with max_d = 20
2588 s16 more = max_spread_amount - max_tunnel_diameter/2 - insure;
2589 ar += v3s16(1,0,1) * more * 2;
2590 of -= v3s16(1,0,1) * more;
2592 s16 route_y_min = 0;
2593 //s16 route_y_max = ar.Y-1;
2594 s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2;
2595 if(bruise_surface == false)
2597 // Don't go through surface too often
2598 route_y_max -= myrand_range(0, max_tunnel_diameter);
2600 route_y_max = rangelim(route_y_max, 0, ar.Y-1);
2604 /*// Minimum is at y=0
2605 route_y_min = -of.Y - 0;*/
2606 // Minimum is at y=max_tunnel_diameter/4
2607 //route_y_min = -of.Y + max_tunnel_diameter/4;
2608 //s16 min = -of.Y + max_tunnel_diameter/4;
2609 s16 min = -of.Y + 0;
2610 route_y_min = myrand_range(min, min + max_tunnel_diameter);
2611 route_y_min = rangelim(route_y_min, 0, route_y_max);
2614 /*dstream<<"route_y_min = "<<route_y_min
2615 <<", route_y_max = "<<route_y_max<<std::endl;*/
2617 // Randomize starting position
2619 (float)(myrand()%ar.X)+0.5,
2620 (float)(myrand_range(route_y_min, route_y_max))+0.5,
2621 (float)(myrand()%ar.Z)+0.5
2624 MapNode airnode(CONTENT_AIR);
2627 Generate some tunnel starting from orp
2630 for(u16 j=0; j<tunnel_routepoints; j++)
2632 v3s16 maxlen(20, 10, 20);
2636 maxlen = v3s16(60,60,60);
2640 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2641 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2642 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2647 else if(rp.X >= ar.X)
2649 if(rp.Y < route_y_min)
2651 else if(rp.Y >= route_y_max)
2652 rp.Y = route_y_max-1;
2655 else if(rp.Z >= ar.Z)
2660 s16 min_d = min_tunnel_diameter;
2661 s16 max_d = max_tunnel_diameter;
2662 s16 rs = myrand_range(min_d, max_d);
2664 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2666 v3f fp = orp + vec * f;
2667 v3s16 cp(fp.X, fp.Y, fp.Z);
2670 s16 d1 = d0 + rs - 1;
2671 for(s16 z0=d0; z0<=d1; z0++)
2673 s16 si = rs - MYMAX(0, abs(z0)-rs/4);
2674 for(s16 x0=-si; x0<=si-1; x0++)
2676 s16 maxabsxz = MYMAX(abs(x0), abs(z0));
2677 s16 si2 = rs - MYMAX(0, maxabsxz-rs/4);
2678 //s16 si2 = rs - abs(x0);
2679 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2685 /*if(isInArea(p, ar) == false)
2687 // Check only height
2688 if(y < 0 || y >= ar.Y)
2692 //assert(vmanip.m_area.contains(p));
2693 if(vmanip.m_area.contains(p) == false)
2695 dstream<<"WARNING: "<<__FUNCTION_NAME
2696 <<":"<<__LINE__<<": "
2697 <<"point not in area"
2702 // Just set it to air, it will be changed to
2704 u32 i = vmanip.m_area.index(p);
2705 vmanip.m_data[i] = airnode;
2719 //TimeTaker timer1("ore veins");
2724 for(u32 jj=0; jj<relative_volume/2000; jj++)
2726 s16 max_vein_diameter = 3;
2728 // Allowed route area size in nodes
2730 sectorpos_base_size*MAP_BLOCKSIZE,
2731 h_blocks*MAP_BLOCKSIZE,
2732 sectorpos_base_size*MAP_BLOCKSIZE
2735 // Area starting point in nodes
2737 sectorpos_base.X*MAP_BLOCKSIZE,
2738 y_blocks_min*MAP_BLOCKSIZE,
2739 sectorpos_base.Y*MAP_BLOCKSIZE
2743 //(this should be more than the maximum radius of the tunnel)
2745 s16 more = max_spread_amount - max_vein_diameter/2 - insure;
2746 ar += v3s16(1,0,1) * more * 2;
2747 of -= v3s16(1,0,1) * more;
2749 // Randomize starting position
2751 (float)(myrand()%ar.X)+0.5,
2752 (float)(myrand()%ar.Y)+0.5,
2753 (float)(myrand()%ar.Z)+0.5
2756 // Randomize mineral
2757 u8 mineral = myrand_range(1, MINERAL_COUNT-1);
2760 Generate some vein starting from orp
2763 for(u16 j=0; j<2; j++)
2766 (float)(myrand()%ar.X)+0.5,
2767 (float)(myrand()%ar.Y)+0.5,
2768 (float)(myrand()%ar.Z)+0.5
2770 v3f vec = rp - orp;*/
2772 v3s16 maxlen(10, 10, 10);
2774 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2775 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2776 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2781 else if(rp.X >= ar.X)
2785 else if(rp.Y >= ar.Y)
2789 else if(rp.Z >= ar.Z)
2795 s16 max_d = max_vein_diameter;
2796 s16 rs = myrand_range(min_d, max_d);
2798 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2800 v3f fp = orp + vec * f;
2801 v3s16 cp(fp.X, fp.Y, fp.Z);
2803 s16 d1 = d0 + rs - 1;
2804 for(s16 z0=d0; z0<=d1; z0++)
2806 s16 si = rs - abs(z0);
2807 for(s16 x0=-si; x0<=si-1; x0++)
2809 s16 si2 = rs - abs(x0);
2810 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2812 // Don't put mineral to every place
2820 /*if(isInArea(p, ar) == false)
2822 // Check only height
2823 if(y < 0 || y >= ar.Y)
2827 assert(vmanip.m_area.contains(p));
2829 // Just set it to air, it will be changed to
2831 u32 i = vmanip.m_area.index(p);
2832 MapNode *n = &vmanip.m_data[i];
2833 if(n->d == CONTENT_STONE)
2848 //TimeTaker timer1("add mud");
2851 Add mud to the central chunk
2854 //s16 mud_add_amount = myrand_range(2, 4);
2855 //s16 mud_add_amount = 0;
2857 for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
2858 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)
2860 // Node position in 2d
2861 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2863 // Randomize mud amount
2864 s16 mud_add_amount = (s16)(3.5 + 2. * noise2d_perlin(
2865 0.5+(float)p2d.X/200, 0.5+(float)p2d.Y/200,
2866 m_seed+1, 3, 0.55));
2868 // Find ground level
2869 s16 surface_y = find_ground_level(vmanip, p2d);
2872 If topmost node is grass, change it to mud.
2873 It might be if it was flown to there from a neighboring
2874 chunk and then converted.
2877 u32 i = vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y));
2878 MapNode *n = &vmanip.m_data[i];
2879 if(n->d == CONTENT_GRASS)
2888 v3s16 em = vmanip.m_area.getExtent();
2889 s16 y_start = surface_y+1;
2890 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2891 for(s16 y=y_start; y<=y_nodes_max; y++)
2893 if(mudcount >= mud_add_amount)
2896 MapNode &n = vmanip.m_data[i];
2900 vmanip.m_area.add_y(em, i, 1);
2909 //TimeTaker timer1("flow mud");
2912 Flow mud away from steep edges
2915 // Iterate a few times
2916 for(s16 k=0; k<3; k++)
2919 /*for(s16 x=0-max_spread_amount+1;
2920 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2922 for(s16 z=0-max_spread_amount+1;
2923 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2927 Firstly, limit area by 1 because mud is flown into neighbors.
2928 Secondly, limit by 1 more to not obstruct sunlight at borders,
2929 because it would fuck up lighting in some places because we're
2930 leaving out removing light from the borders for optimization
2933 /*for(s16 x=0-max_spread_amount+2;
2934 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-2;
2936 for(s16 z=0-max_spread_amount+2;
2937 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-2;
2939 for(s16 x=0-max_spread_amount+1;
2940 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2942 for(s16 z=0-max_spread_amount+1;
2943 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2946 // Node position in 2d
2947 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2949 v3s16 em = vmanip.m_area.getExtent();
2950 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2957 for(; y>=y_nodes_min; y--)
2959 n = &vmanip.m_data[i];
2960 //if(content_walkable(n->d))
2962 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2965 vmanip.m_area.add_y(em, i, -1);
2968 // Stop if out of area
2969 //if(vmanip.m_area.contains(i) == false)
2973 /*// If not mud, do nothing to it
2974 MapNode *n = &vmanip.m_data[i];
2975 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
2979 Don't flow it if the stuff under it is not mud
2983 vmanip.m_area.add_y(em, i2, -1);
2984 // Cancel if out of area
2985 if(vmanip.m_area.contains(i2) == false)
2987 MapNode *n2 = &vmanip.m_data[i2];
2988 if(n2->d != CONTENT_MUD && n2->d != CONTENT_GRASS)
2992 // Make it exactly mud
2995 /*s16 recurse_count = 0;
2999 v3s16(0,0,1), // back
3000 v3s16(1,0,0), // right
3001 v3s16(0,0,-1), // front
3002 v3s16(-1,0,0), // left
3005 // Theck that upper is air or doesn't exist.
3006 // Cancel dropping if upper keeps it in place
3008 vmanip.m_area.add_y(em, i3, 1);
3009 if(vmanip.m_area.contains(i3) == true
3010 && content_walkable(vmanip.m_data[i3].d) == true)
3017 for(u32 di=0; di<4; di++)
3019 v3s16 dirp = dirs4[di];
3022 vmanip.m_area.add_p(em, i2, dirp);
3023 // Fail if out of area
3024 if(vmanip.m_area.contains(i2) == false)
3026 // Check that side is air
3027 MapNode *n2 = &vmanip.m_data[i2];
3028 if(content_walkable(n2->d))
3030 // Check that under side is air
3031 vmanip.m_area.add_y(em, i2, -1);
3032 // Fail if out of area
3033 if(vmanip.m_area.contains(i2) == false)
3035 n2 = &vmanip.m_data[i2];
3036 if(content_walkable(n2->d))
3038 // Loop further down until not air
3040 vmanip.m_area.add_y(em, i2, -1);
3041 // Fail if out of area
3042 if(vmanip.m_area.contains(i2) == false)
3044 n2 = &vmanip.m_data[i2];
3045 }while(content_walkable(n2->d) == false);
3046 // Loop one up so that we're in air
3047 vmanip.m_area.add_y(em, i2, 1);
3048 n2 = &vmanip.m_data[i2];
3050 // Move mud to new place
3052 // Set old place to be air
3053 *n = MapNode(CONTENT_AIR);
3066 //TimeTaker timer1("add water");
3069 Add water to the central chunk (and a bit more)
3072 for(s16 x=0-max_spread_amount;
3073 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3075 for(s16 z=0-max_spread_amount;
3076 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3079 // Node position in 2d
3080 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3082 // Find ground level
3083 //s16 surface_y = find_ground_level(vmanip, p2d);
3086 If ground level is over water level, skip.
3087 NOTE: This leaves caves near water without water,
3088 which looks especially crappy when the nearby water
3089 won't start flowing either for some reason
3091 /*if(surface_y > WATER_LEVEL)
3098 v3s16 em = vmanip.m_area.getExtent();
3099 u8 light = LIGHT_MAX;
3100 // Start at global water surface level
3101 s16 y_start = WATER_LEVEL;
3102 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3103 MapNode *n = &vmanip.m_data[i];
3105 /*// Add first one to transforming liquid queue, if water
3106 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
3108 v3s16 p = v3s16(p2d.X, y_start, p2d.Y);
3109 m_transforming_liquid.push_back(p);
3112 for(s16 y=y_start; y>=y_nodes_min; y--)
3114 n = &vmanip.m_data[i];
3116 // Stop when there is no water and no air
3117 if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
3118 && n->d != CONTENT_WATER)
3120 /*// Add bottom one to transforming liquid queue
3121 vmanip.m_area.add_y(em, i, 1);
3122 n = &vmanip.m_data[i];
3123 if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
3125 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3126 m_transforming_liquid.push_back(p);
3132 n->d = CONTENT_WATERSOURCE;
3133 n->setLight(LIGHTBANK_DAY, light);
3135 // Add to transforming liquid queue (in case it'd
3137 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3138 m_transforming_liquid.push_back(p);
3141 vmanip.m_area.add_y(em, i, -1);
3153 {//TimeTaker timer1("convert mud to sand");
3159 //s16 mud_add_amount = myrand_range(2, 4);
3160 //s16 mud_add_amount = 0;
3162 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3163 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3164 for(s16 x=0-max_spread_amount+1;
3165 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3167 for(s16 z=0-max_spread_amount+1;
3168 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3171 // Node position in 2d
3172 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3174 // Determine whether to have sand here
3175 double sandnoise = noise2d_perlin(
3176 0.5+(float)p2d.X/500, 0.5+(float)p2d.Y/500,
3177 m_seed+59420, 3, 0.50);
3179 bool have_sand = (sandnoise > 0.0);
3181 if(have_sand == false)
3184 // Find ground level
3185 s16 surface_y = find_ground_level_clever(vmanip, p2d);
3187 if(surface_y > WATER_LEVEL + 2)
3191 v3s16 em = vmanip.m_area.getExtent();
3192 s16 y_start = surface_y;
3193 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3194 u32 not_sand_counter = 0;
3195 for(s16 y=y_start; y>=y_nodes_min; y--)
3197 MapNode *n = &vmanip.m_data[i];
3198 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
3199 n->d = CONTENT_SAND;
3203 if(not_sand_counter > 3)
3207 vmanip.m_area.add_y(em, i, -1);
3216 //TimeTaker timer1("plant trees");
3222 // Divide area into parts
3224 s16 sidelen = sectorpos_base_size*MAP_BLOCKSIZE / div;
3225 double area = sidelen * sidelen;
3226 for(s16 x0=0; x0<div; x0++)
3227 for(s16 z0=0; z0<div; z0++)
3229 // Center position of part of division
3231 sectorpos_base.X*MAP_BLOCKSIZE + sidelen/2 + sidelen*x0,
3232 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen/2 + sidelen*z0
3234 // Minimum edge of part of division
3236 sectorpos_base.X*MAP_BLOCKSIZE + sidelen*x0,
3237 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen*z0
3239 // Maximum edge of part of division
3241 sectorpos_base.X*MAP_BLOCKSIZE + sidelen + sidelen*x0 - 1,
3242 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen + sidelen*z0 - 1
3245 u32 tree_count = area * tree_amount_2d(m_seed, p2d_center);
3246 // Put trees in random places on part of division
3247 for(u32 i=0; i<tree_count; i++)
3249 s16 x = myrand_range(p2d_min.X, p2d_max.X);
3250 s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
3251 s16 y = find_ground_level(vmanip, v2s16(x,z));
3252 // Don't make a tree under water level
3257 Trees grow only on mud and grass
3260 u32 i = vmanip.m_area.index(v3s16(p));
3261 MapNode *n = &vmanip.m_data[i];
3262 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
3267 make_tree(vmanip, p);
3270 /*u32 tree_max = relative_area / 60;
3271 //u32 count = myrand_range(0, tree_max);
3272 for(u32 i=0; i<count; i++)
3274 s16 x = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
3275 s16 z = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
3276 x += sectorpos_base.X*MAP_BLOCKSIZE;
3277 z += sectorpos_base.Y*MAP_BLOCKSIZE;
3278 s16 y = find_ground_level(vmanip, v2s16(x,z));
3279 // Don't make a tree under water level
3284 make_tree(vmanip, p);
3292 //TimeTaker timer1("grow grass");
3298 /*for(s16 x=0-4; x<sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
3299 for(s16 z=0-4; z<sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
3300 for(s16 x=0-max_spread_amount;
3301 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3303 for(s16 z=0-max_spread_amount;
3304 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3307 // Node position in 2d
3308 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3311 Find the lowest surface to which enough light ends up
3314 Basically just wait until not air and not leaves.
3318 v3s16 em = vmanip.m_area.getExtent();
3319 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
3321 // Go to ground level
3322 for(y=y_nodes_max; y>=y_nodes_min; y--)
3324 MapNode &n = vmanip.m_data[i];
3325 if(n.d != CONTENT_AIR
3326 && n.d != CONTENT_LEAVES)
3328 vmanip.m_area.add_y(em, i, -1);
3330 if(y >= y_nodes_min)
3333 surface_y = y_nodes_min;
3336 u32 i = vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
3337 MapNode *n = &vmanip.m_data[i];
3338 if(n->d == CONTENT_MUD)
3339 n->d = CONTENT_GRASS;
3348 core::map<v3s16, bool> light_sources;
3351 // 750ms @cs=8, can't optimize more
3352 //TimeTaker timer1("initial lighting");
3356 Go through the edges and add all nodes that have light to light_sources
3360 for(s16 i=0; i<4; i++)
3362 for(s16 j=lighting_min_d;
3369 if(i == 0 || i == 1)
3371 x = (i==0) ? lighting_min_d : lighting_max_d;
3380 z = (i==0) ? lighting_min_d : lighting_max_d;
3387 // Node position in 2d
3388 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3391 v3s16 em = vmanip.m_area.getExtent();
3392 s16 y_start = y_nodes_max;
3393 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3394 for(s16 y=y_start; y>=y_nodes_min; y--)
3396 MapNode *n = &vmanip.m_data[i];
3397 if(n->getLight(LIGHTBANK_DAY) != 0)
3399 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3406 /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3407 for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3408 /*for(s16 x=0-max_spread_amount+1;
3409 x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3411 for(s16 z=0-max_spread_amount+1;
3412 z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3416 This has to be 1 smaller than the actual area, because
3417 neighboring nodes are checked.
3419 for(s16 x=lighting_min_d+1;
3420 x<=lighting_max_d-1;
3422 for(s16 z=lighting_min_d+1;
3423 z<=lighting_max_d-1;
3426 // Node position in 2d
3427 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3430 Apply initial sunlight
3433 u8 light = LIGHT_SUN;
3434 bool add_to_sources = false;
3435 v3s16 em = vmanip.m_area.getExtent();
3436 s16 y_start = y_nodes_max;
3437 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3438 for(s16 y=y_start; y>=y_nodes_min; y--)
3440 MapNode *n = &vmanip.m_data[i];
3442 if(light_propagates_content(n->d) == false)
3446 else if(light != LIGHT_SUN
3447 || sunlight_propagates_content(n->d) == false)
3453 // This doesn't take much time
3454 if(add_to_sources == false)
3457 Check sides. If side is not air or water, start
3458 adding to light_sources.
3461 v3s16(0,0,1), // back
3462 v3s16(1,0,0), // right
3463 v3s16(0,0,-1), // front
3464 v3s16(-1,0,0), // left
3466 for(u32 di=0; di<4; di++)
3468 v3s16 dirp = dirs4[di];
3470 vmanip.m_area.add_p(em, i2, dirp);
3471 MapNode *n2 = &vmanip.m_data[i2];
3473 n2->d != CONTENT_AIR
3474 && n2->d != CONTENT_WATERSOURCE
3475 && n2->d != CONTENT_WATER
3477 add_to_sources = true;
3483 n->setLight(LIGHTBANK_DAY, light);
3484 n->setLight(LIGHTBANK_NIGHT, 0);
3486 // This doesn't take much time
3487 if(light != 0 && add_to_sources)
3489 // Insert light source
3490 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3493 // Increment index by y
3494 vmanip.m_area.add_y(em, i, -1);
3501 // Spread light around
3503 TimeTaker timer("generateChunkRaw() spreadLight");
3504 vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
3511 timer_generate.stop();
3514 Blit generated stuff to map
3518 //TimeTaker timer("generateChunkRaw() blitBackAll");
3519 vmanip.blitBackAll(&changed_blocks);
3522 Update day/night difference cache of the MapBlocks
3525 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
3526 i.atEnd() == false; i++)
3528 MapBlock *block = i.getNode()->getValue();
3529 block->updateDayNightDiff();
3535 Create chunk metadata
3538 for(s16 x=-1; x<=1; x++)
3539 for(s16 y=-1; y<=1; y++)
3541 v2s16 chunkpos0 = chunkpos + v2s16(x,y);
3542 // Add chunk meta information
3543 MapChunk *chunk = getChunk(chunkpos0);
3546 chunk = new MapChunk();
3547 m_chunks.insert(chunkpos0, chunk);
3549 //chunk->setIsVolatile(true);
3550 if(chunk->getGenLevel() > GENERATED_PARTLY)
3551 chunk->setGenLevel(GENERATED_PARTLY);
3555 Set central chunk non-volatile and return it
3557 MapChunk *chunk = getChunk(chunkpos);
3560 //chunk->setIsVolatile(false);
3561 chunk->setGenLevel(GENERATED_FULLY);
3566 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
3567 core::map<v3s16, MapBlock*> &changed_blocks)
3569 dstream<<"generateChunk(): Generating chunk "
3570 <<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
3573 /*for(s16 x=-1; x<=1; x++)
3574 for(s16 y=-1; y<=1; y++)*/
3575 for(s16 x=-0; x<=0; x++)
3576 for(s16 y=-0; y<=0; y++)
3578 v2s16 chunkpos0 = chunkpos1 + v2s16(x,y);
3579 MapChunk *chunk = getChunk(chunkpos0);
3580 // Skip if already generated
3581 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3583 generateChunkRaw(chunkpos0, changed_blocks);
3586 assert(chunkNonVolatile(chunkpos1));
3588 MapChunk *chunk = getChunk(chunkpos1);
3592 ServerMapSector * ServerMap::createSector(v2s16 p2d)
3594 DSTACK("%s: p2d=(%d,%d)",
3599 Check if it exists already in memory
3601 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3606 Try to load it from disk (with blocks)
3608 if(loadSectorFull(p2d) == true)
3610 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3613 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
3614 throw InvalidPositionException("");
3620 If there is no master heightmap, throw.
3622 if(m_heightmap == NULL)
3624 throw InvalidPositionException("createSector(): no heightmap");
3628 Do not create over-limit
3630 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3631 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3632 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3633 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3634 throw InvalidPositionException("createSector(): pos. over limit");
3637 Generate blank sector
3640 // Number of heightmaps in sector in each direction
3641 u16 hm_split = SECTOR_HEIGHTMAP_SPLIT;
3643 // Heightmap side width
3644 s16 hm_d = MAP_BLOCKSIZE / hm_split;
3646 sector = new ServerMapSector(this, p2d, hm_split);
3648 // Sector position on map in nodes
3649 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3651 /*dstream<<"Generating sector ("<<p2d.X<<","<<p2d.Y<<")"
3652 " heightmaps and objects"<<std::endl;*/
3655 Generate sector heightmap
3658 v2s16 mhm_p = p2d * hm_split;
3659 /*f32 corners[4] = {
3660 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)*hm_split),
3661 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)*hm_split),
3662 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)*hm_split),
3663 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)*hm_split),
3666 // Loop through sub-heightmaps
3667 for(s16 y=0; y<hm_split; y++)
3668 for(s16 x=0; x<hm_split; x++)
3670 v2s16 p_in_sector = v2s16(x,y);
3671 v2s16 mhm_p = p2d * hm_split + p_in_sector;
3673 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)),
3674 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)),
3675 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)),
3676 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)),
3679 /*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
3680 <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
3683 FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
3685 sector->setHeightmap(p_in_sector, hm);
3687 //hm->generateContinued(1.0, 0.5, corners);
3688 hm->generateContinued(0.5, 0.5, corners);
3693 // Add dummy objects
3694 core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
3695 sector->setObjects(objects);
3700 m_sectors.insert(p2d, sector);
3705 MapSector * ServerMap::emergeSector(v2s16 p2d,
3706 core::map<v3s16, MapBlock*> &changed_blocks)
3708 DSTACK("%s: p2d=(%d,%d)",
3715 v2s16 chunkpos = sector_to_chunk(p2d);
3716 /*bool chunk_nonvolatile = false;
3717 MapChunk *chunk = getChunk(chunkpos);
3718 if(chunk && chunk->getIsVolatile() == false)
3719 chunk_nonvolatile = true;*/
3720 bool chunk_nonvolatile = chunkNonVolatile(chunkpos);
3723 If chunk is not fully generated, generate chunk
3725 if(chunk_nonvolatile == false)
3727 // Generate chunk and neighbors
3728 generateChunk(chunkpos, changed_blocks);
3732 Return sector if it exists now
3734 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3739 Try to load it from disk
3741 if(loadSectorFull(p2d) == true)
3743 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3746 dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
3747 throw InvalidPositionException("");
3753 generateChunk should have generated the sector
3760 //return generateSector();
3764 NOTE: This is not used for main map generation, only for blocks
3765 that are very high or low
3767 MapBlock * ServerMap::generateBlock(
3769 MapBlock *original_dummy,
3770 ServerMapSector *sector,
3771 core::map<v3s16, MapBlock*> &changed_blocks,
3772 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
3775 DSTACK("%s: p=(%d,%d,%d)",
3779 /*dstream<<"generateBlock(): "
3780 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3783 MapBlock *block = original_dummy;
3785 v2s16 p2d(p.X, p.Z);
3789 Do not generate over-limit
3791 if(blockpos_over_limit(p))
3793 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
3794 throw InvalidPositionException("generateBlock(): pos. over limit");
3798 If block doesn't exist, create one.
3799 If it exists, it is a dummy. In that case unDummify() it.
3801 NOTE: This already sets the map as the parent of the block
3805 block = sector->createBlankBlockNoInsert(block_y);
3809 // Remove the block so that nobody can get a half-generated one.
3810 sector->removeBlock(block);
3811 // Allocate the block to contain the generated data
3815 /*u8 water_material = CONTENT_WATER;
3816 if(g_settings.getBool("endless_water"))
3817 water_material = CONTENT_WATERSOURCE;*/
3818 u8 water_material = CONTENT_WATERSOURCE;
3820 s32 lowest_ground_y = 32767;
3821 s32 highest_ground_y = -32768;
3824 //sector->printHeightmaps();
3826 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3827 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3829 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
3831 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
3832 //assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
3833 if(surface_y_f < GROUNDHEIGHT_VALID_MINVALUE)
3835 dstream<<"WARNING: Surface height not found in sector "
3836 "for block that is being emerged"<<std::endl;
3840 s16 surface_y = surface_y_f;
3841 //avg_ground_y += surface_y;
3842 if(surface_y < lowest_ground_y)
3843 lowest_ground_y = surface_y;
3844 if(surface_y > highest_ground_y)
3845 highest_ground_y = surface_y;
3847 s32 surface_depth = 0;
3849 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
3851 //float min_slope = 0.45;
3852 //float max_slope = 0.85;
3853 float min_slope = 0.60;
3854 float max_slope = 1.20;
3855 float min_slope_depth = 5.0;
3856 float max_slope_depth = 0;
3858 if(slope < min_slope)
3859 surface_depth = min_slope_depth;
3860 else if(slope > max_slope)
3861 surface_depth = max_slope_depth;
3863 surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
3865 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3867 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
3872 NOTE: If there are some man-made structures above the
3873 newly created block, they won't be taken into account.
3875 if(real_y > surface_y)
3876 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
3882 // If node is over heightmap y, it's air or water
3883 if(real_y > surface_y)
3885 // If under water level, it's water
3886 if(real_y < WATER_LEVEL)
3888 n.d = water_material;
3889 n.setLight(LIGHTBANK_DAY,
3890 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
3892 Add to transforming liquid queue (in case it'd
3895 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
3896 m_transforming_liquid.push_back(real_pos);
3902 // Else it's ground or dungeons (air)
3905 // If it's surface_depth under ground, it's stone
3906 if(real_y <= surface_y - surface_depth)
3908 n.d = CONTENT_STONE;
3912 // It is mud if it is under the first ground
3913 // level or under water
3914 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
3920 n.d = CONTENT_GRASS;
3923 //n.d = CONTENT_MUD;
3925 /*// If under water level, it's mud
3926 if(real_y < WATER_LEVEL)
3928 // Only the topmost node is grass
3929 else if(real_y <= surface_y - 1)
3932 n.d = CONTENT_GRASS;*/
3936 block->setNode(v3s16(x0,y0,z0), n);
3941 Calculate some helper variables
3944 // Completely underground if the highest part of block is under lowest
3946 // This has to be very sure; it's probably one too strict now but
3947 // that's just better.
3948 bool completely_underground =
3949 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
3951 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
3953 bool mostly_underwater_surface = false;
3954 if(highest_ground_y < WATER_LEVEL
3955 && some_part_underground && !completely_underground)
3956 mostly_underwater_surface = true;
3959 Get local attributes
3962 //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
3964 float caves_amount = 0.5;
3969 NOTE: BEWARE: Too big amount of attribute points slows verything
3971 1 interpolation from 5000 points takes 2-3ms.
3973 //TimeTaker timer("generateBlock() local attribute retrieval");
3974 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3975 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
3976 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
3980 //dstream<<"generateBlock(): Done"<<std::endl;
3986 // Initialize temporary table
3987 const s32 ued = MAP_BLOCKSIZE;
3988 bool underground_emptiness[ued*ued*ued];
3989 for(s32 i=0; i<ued*ued*ued; i++)
3991 underground_emptiness[i] = 0;
3998 Initialize orp and ors. Try to find if some neighboring
3999 MapBlock has a tunnel ended in its side
4003 (float)(myrand()%ued)+0.5,
4004 (float)(myrand()%ued)+0.5,
4005 (float)(myrand()%ued)+0.5
4008 bool found_existing = false;
4014 for(s16 y=0; y<ued; y++)
4015 for(s16 x=0; x<ued; x++)
4017 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4018 if(getNode(ap).d == CONTENT_AIR)
4020 orp = v3f(x+1,y+1,0);
4021 found_existing = true;
4022 goto continue_generating;
4026 catch(InvalidPositionException &e){}
4032 for(s16 y=0; y<ued; y++)
4033 for(s16 x=0; x<ued; x++)
4035 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4036 if(getNode(ap).d == CONTENT_AIR)
4038 orp = v3f(x+1,y+1,ued-1);
4039 found_existing = true;
4040 goto continue_generating;
4044 catch(InvalidPositionException &e){}
4050 for(s16 y=0; y<ued; y++)
4051 for(s16 z=0; z<ued; z++)
4053 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4054 if(getNode(ap).d == CONTENT_AIR)
4056 orp = v3f(0,y+1,z+1);
4057 found_existing = true;
4058 goto continue_generating;
4062 catch(InvalidPositionException &e){}
4068 for(s16 y=0; y<ued; y++)
4069 for(s16 z=0; z<ued; z++)
4071 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4072 if(getNode(ap).d == CONTENT_AIR)
4074 orp = v3f(ued-1,y+1,z+1);
4075 found_existing = true;
4076 goto continue_generating;
4080 catch(InvalidPositionException &e){}
4086 for(s16 x=0; x<ued; x++)
4087 for(s16 z=0; z<ued; z++)
4089 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4090 if(getNode(ap).d == CONTENT_AIR)
4092 orp = v3f(x+1,0,z+1);
4093 found_existing = true;
4094 goto continue_generating;
4098 catch(InvalidPositionException &e){}
4104 for(s16 x=0; x<ued; x++)
4105 for(s16 z=0; z<ued; z++)
4107 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4108 if(getNode(ap).d == CONTENT_AIR)
4110 orp = v3f(x+1,ued-1,z+1);
4111 found_existing = true;
4112 goto continue_generating;
4116 catch(InvalidPositionException &e){}
4118 continue_generating:
4121 Choose whether to actually generate dungeon
4123 bool do_generate_dungeons = true;
4124 // Don't generate if no part is underground
4125 if(!some_part_underground)
4127 do_generate_dungeons = false;
4129 // Don't generate if mostly underwater surface
4130 /*else if(mostly_underwater_surface)
4132 do_generate_dungeons = false;
4134 // Partly underground = cave
4135 else if(!completely_underground)
4137 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
4139 // Found existing dungeon underground
4140 else if(found_existing && completely_underground)
4142 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
4144 // Underground and no dungeons found
4147 do_generate_dungeons = (rand() % 300 <= (s32)(caves_amount*100));
4150 if(do_generate_dungeons)
4153 Generate some tunnel starting from orp and ors
4155 for(u16 i=0; i<3; i++)
4158 (float)(myrand()%ued)+0.5,
4159 (float)(myrand()%ued)+0.5,
4160 (float)(myrand()%ued)+0.5
4164 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
4168 for(float f=0; f<1.0; f+=0.04)
4170 v3f fp = orp + vec * f;
4171 v3s16 cp(fp.X, fp.Y, fp.Z);
4173 s16 d1 = d0 + rs - 1;
4174 for(s16 z0=d0; z0<=d1; z0++)
4176 s16 si = rs - abs(z0);
4177 for(s16 x0=-si; x0<=si-1; x0++)
4179 s16 si2 = rs - abs(x0);
4180 for(s16 y0=-si2+1; y0<=si2-1; y0++)
4186 if(isInArea(p, ued) == false)
4188 underground_emptiness[ued*ued*z + ued*y + x] = 1;
4200 // Set to true if has caves.
4201 // Set when some non-air is changed to air when making caves.
4202 bool has_dungeons = false;
4205 Apply temporary cave data to block
4208 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4209 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4211 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4213 MapNode n = block->getNode(v3s16(x0,y0,z0));
4216 if(underground_emptiness[
4217 ued*ued*(z0*ued/MAP_BLOCKSIZE)
4218 +ued*(y0*ued/MAP_BLOCKSIZE)
4219 +(x0*ued/MAP_BLOCKSIZE)])
4221 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
4224 has_dungeons = true;
4230 block->setNode(v3s16(x0,y0,z0), n);
4235 This is used for guessing whether or not the block should
4236 receive sunlight from the top if the block above doesn't exist
4238 block->setIsUnderground(completely_underground);
4241 Force lighting update if some part of block is partly
4242 underground and has caves.
4244 /*if(some_part_underground && !completely_underground && has_dungeons)
4246 //dstream<<"Half-ground caves"<<std::endl;
4247 lighting_invalidated_blocks[block->getPos()] = block;
4250 // DEBUG: Always update lighting
4251 //lighting_invalidated_blocks[block->getPos()] = block;
4257 if(some_part_underground)
4259 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
4264 for(s16 i=0; i<underground_level/4 + 1; i++)
4266 if(myrand()%50 == 0)
4269 (myrand()%(MAP_BLOCKSIZE-2))+1,
4270 (myrand()%(MAP_BLOCKSIZE-2))+1,
4271 (myrand()%(MAP_BLOCKSIZE-2))+1
4277 for(u16 i=0; i<27; i++)
4279 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4281 block->setNode(cp+g_27dirs[i], n);
4289 u16 coal_amount = 30.0 * g_settings.getFloat("coal_amount");
4290 u16 coal_rareness = 60 / coal_amount;
4291 if(coal_rareness == 0)
4293 if(myrand()%coal_rareness == 0)
4295 u16 a = myrand() % 16;
4296 u16 amount = coal_amount * a*a*a / 1000;
4297 for(s16 i=0; i<amount; i++)
4300 (myrand()%(MAP_BLOCKSIZE-2))+1,
4301 (myrand()%(MAP_BLOCKSIZE-2))+1,
4302 (myrand()%(MAP_BLOCKSIZE-2))+1
4306 n.d = CONTENT_STONE;
4307 n.param = MINERAL_COAL;
4309 for(u16 i=0; i<27; i++)
4311 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4313 block->setNode(cp+g_27dirs[i], n);
4321 //TODO: change to iron_amount or whatever
4322 u16 iron_amount = 30.0 * g_settings.getFloat("coal_amount");
4323 u16 iron_rareness = 60 / iron_amount;
4324 if(iron_rareness == 0)
4326 if(myrand()%iron_rareness == 0)
4328 u16 a = myrand() % 16;
4329 u16 amount = iron_amount * a*a*a / 1000;
4330 for(s16 i=0; i<amount; i++)
4333 (myrand()%(MAP_BLOCKSIZE-2))+1,
4334 (myrand()%(MAP_BLOCKSIZE-2))+1,
4335 (myrand()%(MAP_BLOCKSIZE-2))+1
4339 n.d = CONTENT_STONE;
4340 n.param = MINERAL_IRON;
4342 for(u16 i=0; i<27; i++)
4344 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4346 block->setNode(cp+g_27dirs[i], n);
4353 Create a few rats in empty blocks underground
4355 if(completely_underground)
4357 //for(u16 i=0; i<2; i++)
4360 (myrand()%(MAP_BLOCKSIZE-2))+1,
4361 (myrand()%(MAP_BLOCKSIZE-2))+1,
4362 (myrand()%(MAP_BLOCKSIZE-2))+1
4365 // Check that the place is empty
4366 //if(!is_ground_content(block->getNode(cp).d))
4369 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
4370 block->addObject(obj);
4376 Add block to sector.
4378 sector->insertBlock(block);
4384 // An y-wise container of changed blocks
4385 core::map<s16, MapBlock*> changed_blocks_sector;
4388 Check if any sector's objects can be placed now.
4391 core::map<v3s16, u8> *objects = sector->getObjects();
4392 core::list<v3s16> objects_to_remove;
4393 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
4394 i.atEnd() == false; i++)
4396 v3s16 p = i.getNode()->getKey();
4398 u8 d = i.getNode()->getValue();
4400 // Ground level point (user for stuff that is on ground)
4402 bool ground_found = true;
4404 // Search real ground level
4408 MapNode n = sector->getNode(gp);
4410 // If not air, go one up and continue to placing the tree
4411 if(n.d != CONTENT_AIR)
4417 // If air, go one down
4418 gp += v3s16(0,-1,0);
4420 }catch(InvalidPositionException &e)
4422 // Ground not found.
4423 ground_found = false;
4424 // This is most close to ground
4431 if(d == SECTOR_OBJECT_TEST)
4433 if(sector->isValidArea(p + v3s16(0,0,0),
4434 p + v3s16(0,0,0), &changed_blocks_sector))
4437 n.d = CONTENT_TORCH;
4438 sector->setNode(p, n);
4439 objects_to_remove.push_back(p);
4442 else if(d == SECTOR_OBJECT_TREE_1)
4444 if(ground_found == false)
4447 v3s16 p_min = gp + v3s16(-1,0,-1);
4448 v3s16 p_max = gp + v3s16(1,5,1);
4449 if(sector->isValidArea(p_min, p_max,
4450 &changed_blocks_sector))
4454 sector->setNode(gp+v3s16(0,0,0), n);
4455 sector->setNode(gp+v3s16(0,1,0), n);
4456 sector->setNode(gp+v3s16(0,2,0), n);
4457 sector->setNode(gp+v3s16(0,3,0), n);
4459 n.d = CONTENT_LEAVES;
4461 if(myrand()%4!=0) sector->setNode(gp+v3s16(0,5,0), n);
4463 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,0), n);
4464 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,0), n);
4465 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,5,-1), n);
4466 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,5,1), n);
4467 /*if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,1), n);
4468 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,1), n);
4469 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,-1), n);
4470 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,-1), n);*/
4472 sector->setNode(gp+v3s16(0,4,0), n);
4474 sector->setNode(gp+v3s16(-1,4,0), n);
4475 sector->setNode(gp+v3s16(1,4,0), n);
4476 sector->setNode(gp+v3s16(0,4,-1), n);
4477 sector->setNode(gp+v3s16(0,4,1), n);
4478 sector->setNode(gp+v3s16(1,4,1), n);
4479 sector->setNode(gp+v3s16(-1,4,1), n);
4480 sector->setNode(gp+v3s16(-1,4,-1), n);
4481 sector->setNode(gp+v3s16(1,4,-1), n);
4483 sector->setNode(gp+v3s16(-1,3,0), n);
4484 sector->setNode(gp+v3s16(1,3,0), n);
4485 sector->setNode(gp+v3s16(0,3,-1), n);
4486 sector->setNode(gp+v3s16(0,3,1), n);
4487 sector->setNode(gp+v3s16(1,3,1), n);
4488 sector->setNode(gp+v3s16(-1,3,1), n);
4489 sector->setNode(gp+v3s16(-1,3,-1), n);
4490 sector->setNode(gp+v3s16(1,3,-1), n);
4492 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,0), n);
4493 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,0), n);
4494 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,2,-1), n);
4495 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,2,1), n);
4496 /*if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,1), n);
4497 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,1), n);
4498 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,-1), n);
4499 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,-1), n);*/
4501 // Objects are identified by wanted position
4502 objects_to_remove.push_back(p);
4504 // Lighting has to be recalculated for this one.
4505 sector->getBlocksInArea(p_min, p_max,
4506 lighting_invalidated_blocks);
4509 else if(d == SECTOR_OBJECT_BUSH_1)
4511 if(ground_found == false)
4514 if(sector->isValidArea(gp + v3s16(0,0,0),
4515 gp + v3s16(0,0,0), &changed_blocks_sector))
4518 n.d = CONTENT_LEAVES;
4519 sector->setNode(gp+v3s16(0,0,0), n);
4521 // Objects are identified by wanted position
4522 objects_to_remove.push_back(p);
4525 else if(d == SECTOR_OBJECT_RAVINE)
4528 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
4529 v3s16 p_max = p + v3s16(6,6,6);
4530 if(sector->isValidArea(p_min, p_max,
4531 &changed_blocks_sector))
4534 n.d = CONTENT_STONE;
4537 s16 depth = maxdepth + (myrand()%10);
4539 s16 minz = -6 - (-2);
4541 for(s16 x=-6; x<=6; x++)
4543 z += -1 + (myrand()%3);
4548 for(s16 y=depth+(myrand()%2); y<=6; y++)
4550 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
4553 v3s16 p2 = p + v3s16(x,y,z-2);
4554 //if(is_ground_content(sector->getNode(p2).d))
4555 if(content_features(sector->getNode(p2).d).walkable)
4556 sector->setNode(p2, n);
4559 v3s16 p2 = p + v3s16(x,y,z-1);
4560 if(content_features(sector->getNode(p2).d).walkable)
4561 sector->setNode(p2, n2);
4564 v3s16 p2 = p + v3s16(x,y,z+0);
4565 if(content_features(sector->getNode(p2).d).walkable)
4566 sector->setNode(p2, n2);
4569 v3s16 p2 = p + v3s16(x,y,z+1);
4570 if(content_features(sector->getNode(p2).d).walkable)
4571 sector->setNode(p2, n);
4574 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
4575 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
4579 objects_to_remove.push_back(p);
4581 // Lighting has to be recalculated for this one.
4582 sector->getBlocksInArea(p_min, p_max,
4583 lighting_invalidated_blocks);
4588 dstream<<"ServerMap::generateBlock(): "
4589 "Invalid heightmap object"
4594 catch(InvalidPositionException &e)
4596 dstream<<"WARNING: "<<__FUNCTION_NAME
4597 <<": while inserting object "<<(int)d
4598 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
4599 <<" InvalidPositionException.what()="
4600 <<e.what()<<std::endl;
4601 // This is not too fatal and seems to happen sometimes.
4606 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
4607 i != objects_to_remove.end(); i++)
4609 objects->remove(*i);
4613 Translate sector's changed blocks to global changed blocks
4616 for(core::map<s16, MapBlock*>::Iterator
4617 i = changed_blocks_sector.getIterator();
4618 i.atEnd() == false; i++)
4620 MapBlock *block = i.getNode()->getValue();
4622 changed_blocks.insert(block->getPos(), block);
4625 block->setLightingExpired(true);
4632 <<"lighting_invalidated_blocks.size()"
4636 <<" "<<lighting_invalidated_blocks.size()
4637 <<", "<<has_dungeons
4638 <<", "<<completely_underground
4639 <<", "<<some_part_underground
4646 MapBlock * ServerMap::createBlock(v3s16 p)
4648 DSTACK("%s: p=(%d,%d,%d)",
4649 __FUNCTION_NAME, p.X, p.Y, p.Z);
4651 v2s16 p2d(p.X, p.Z);
4654 This will create or load a sector if not found in memory.
4655 If block exists on disk, it will be loaded.
4657 NOTE: On old save formats, this will be slow, as it generates
4658 lighting on blocks for them.
4660 ServerMapSector *sector;
4662 sector = (ServerMapSector*)createSector(p2d);
4663 assert(sector->getId() == MAPSECTOR_SERVER);
4665 /*catch(InvalidPositionException &e)
4667 dstream<<"createBlock: createSector() failed"<<std::endl;
4670 catch(std::exception &e)
4672 dstream<<"createBlock: createSector() failed: "
4673 <<e.what()<<std::endl;
4678 Try to get a block from the sector
4681 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4685 block = sector->createBlankBlock(block_y);
4689 MapBlock * ServerMap::emergeBlock(
4691 bool only_from_disk,
4692 core::map<v3s16, MapBlock*> &changed_blocks,
4693 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4696 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
4698 p.X, p.Y, p.Z, only_from_disk);
4700 v2s16 p2d(p.X, p.Z);
4703 This will create or load a sector if not found in memory.
4704 If block exists on disk, it will be loaded.
4706 NOTE: On old save formats, this will be slow, as it generates
4707 lighting on blocks for them.
4709 ServerMapSector *sector;
4711 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
4712 assert(sector->getId() == MAPSECTOR_SERVER);
4714 catch(std::exception &e)
4716 dstream<<"emergeBlock: emergeSector() failed: "
4717 <<e.what()<<std::endl;
4722 Try to get a block from the sector
4725 bool does_not_exist = false;
4726 bool lighting_expired = false;
4727 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4731 does_not_exist = true;
4733 else if(block->isDummy() == true)
4735 does_not_exist = true;
4737 else if(block->getLightingExpired())
4739 lighting_expired = true;
4744 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
4749 If block was not found on disk and not going to generate a
4750 new one, make sure there is a dummy block in place.
4752 if(only_from_disk && (does_not_exist || lighting_expired))
4754 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
4758 // Create dummy block
4759 block = new MapBlock(this, p, true);
4761 // Add block to sector
4762 sector->insertBlock(block);
4768 //dstream<<"Not found on disk, generating."<<std::endl;
4770 //TimeTaker("emergeBlock() generate");
4772 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
4775 If the block doesn't exist, generate the block.
4779 block = generateBlock(p, block, sector, changed_blocks,
4780 lighting_invalidated_blocks);
4783 if(lighting_expired)
4785 lighting_invalidated_blocks.insert(p, block);
4789 Initially update sunlight
4793 core::map<v3s16, bool> light_sources;
4794 bool black_air_left = false;
4795 bool bottom_invalid =
4796 block->propagateSunlight(light_sources, true,
4797 &black_air_left, true);
4799 // If sunlight didn't reach everywhere and part of block is
4800 // above ground, lighting has to be properly updated
4801 //if(black_air_left && some_part_underground)
4804 lighting_invalidated_blocks[block->getPos()] = block;
4809 lighting_invalidated_blocks[block->getPos()] = block;
4814 Debug mode operation
4816 bool haxmode = g_settings.getBool("haxmode");
4819 // Don't calculate lighting at all
4820 //lighting_invalidated_blocks.clear();
4826 void ServerMap::createDir(std::string path)
4828 if(fs::CreateDir(path) == false)
4830 m_dout<<DTIME<<"ServerMap: Failed to create directory "
4831 <<"\""<<path<<"\""<<std::endl;
4832 throw BaseException("ServerMap failed to create directory");
4836 std::string ServerMap::getSectorSubDir(v2s16 pos)
4839 snprintf(cc, 9, "%.4x%.4x",
4840 (unsigned int)pos.X&0xffff,
4841 (unsigned int)pos.Y&0xffff);
4843 return std::string(cc);
4846 std::string ServerMap::getSectorDir(v2s16 pos)
4848 return m_savedir + "/sectors/" + getSectorSubDir(pos);
4851 v2s16 ServerMap::getSectorPos(std::string dirname)
4853 if(dirname.size() != 8)
4854 throw InvalidFilenameException("Invalid sector directory name");
4856 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
4858 throw InvalidFilenameException("Invalid sector directory name");
4859 v2s16 pos((s16)x, (s16)y);
4863 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
4865 v2s16 p2d = getSectorPos(sectordir);
4867 if(blockfile.size() != 4){
4868 throw InvalidFilenameException("Invalid block filename");
4871 int r = sscanf(blockfile.c_str(), "%4x", &y);
4873 throw InvalidFilenameException("Invalid block filename");
4874 return v3s16(p2d.X, y, p2d.Y);
4878 #define ENABLE_SECTOR_SAVING 1
4879 #define ENABLE_SECTOR_LOADING 1
4880 #define ENABLE_BLOCK_SAVING 1
4881 #define ENABLE_BLOCK_LOADING 1
4883 void ServerMap::save(bool only_changed)
4885 DSTACK(__FUNCTION_NAME);
4886 if(m_map_saving_enabled == false)
4888 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
4892 if(only_changed == false)
4893 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
4896 saveMasterHeightmap();
4898 u32 sector_meta_count = 0;
4899 u32 block_count = 0;
4902 JMutexAutoLock lock(m_sector_mutex);
4904 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
4905 for(; i.atEnd() == false; i++)
4907 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
4908 assert(sector->getId() == MAPSECTOR_SERVER);
4910 if(ENABLE_SECTOR_SAVING)
4912 if(sector->differs_from_disk || only_changed == false)
4914 saveSectorMeta(sector);
4915 sector_meta_count++;
4918 if(ENABLE_BLOCK_SAVING)
4920 core::list<MapBlock*> blocks;
4921 sector->getBlocks(blocks);
4922 core::list<MapBlock*>::Iterator j;
4923 for(j=blocks.begin(); j!=blocks.end(); j++)
4925 MapBlock *block = *j;
4926 if(block->getChangedFlag() || only_changed == false)
4931 /*dstream<<"ServerMap: Written block ("
4932 <<block->getPos().X<<","
4933 <<block->getPos().Y<<","
4934 <<block->getPos().Z<<")"
4944 Only print if something happened or saved whole map
4946 if(only_changed == false || sector_meta_count != 0
4947 || block_count != 0)
4949 dstream<<DTIME<<"ServerMap: Written: "
4950 <<sector_meta_count<<" sector metadata files, "
4951 <<block_count<<" block files"
4956 void ServerMap::loadAll()
4958 DSTACK(__FUNCTION_NAME);
4959 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
4961 loadMasterHeightmap();
4963 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
4965 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
4967 JMutexAutoLock lock(m_sector_mutex);
4970 s32 printed_counter = -100000;
4971 s32 count = list.size();
4973 std::vector<fs::DirListNode>::iterator i;
4974 for(i=list.begin(); i!=list.end(); i++)
4976 if(counter > printed_counter + 10)
4978 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
4979 printed_counter = counter;
4983 MapSector *sector = NULL;
4985 // We want directories
4989 sector = loadSectorMeta(i->name);
4991 catch(InvalidFilenameException &e)
4993 // This catches unknown crap in directory
4996 if(ENABLE_BLOCK_LOADING)
4998 std::vector<fs::DirListNode> list2 = fs::GetDirListing
4999 (m_savedir+"/sectors/"+i->name);
5000 std::vector<fs::DirListNode>::iterator i2;
5001 for(i2=list2.begin(); i2!=list2.end(); i2++)
5007 loadBlock(i->name, i2->name, sector);
5009 catch(InvalidFilenameException &e)
5011 // This catches unknown crap in directory
5016 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
5019 void ServerMap::saveMasterHeightmap()
5021 DSTACK(__FUNCTION_NAME);
5022 createDir(m_savedir);
5024 std::string fullpath = m_savedir + "/master_heightmap";
5025 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5026 if(o.good() == false)
5027 throw FileNotGoodException("Cannot open master heightmap");
5029 // Format used for writing
5030 u8 version = SER_FMT_VER_HIGHEST;
5033 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
5035 [0] u8 serialization version
5036 [1] X master heightmap
5038 u32 fullsize = 1 + hmdata.getSize();
5039 SharedBuffer<u8> data(fullsize);
5042 memcpy(&data[1], *hmdata, hmdata.getSize());
5044 o.write((const char*)*data, fullsize);
5047 m_heightmap->serialize(o, version);
5050 void ServerMap::loadMasterHeightmap()
5052 DSTACK(__FUNCTION_NAME);
5053 std::string fullpath = m_savedir + "/master_heightmap";
5054 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5055 if(is.good() == false)
5056 throw FileNotGoodException("Cannot open master heightmap");
5058 if(m_heightmap != NULL)
5061 m_heightmap = UnlimitedHeightmap::deSerialize(is, &m_padb);
5064 void ServerMap::saveSectorMeta(ServerMapSector *sector)
5066 DSTACK(__FUNCTION_NAME);
5067 // Format used for writing
5068 u8 version = SER_FMT_VER_HIGHEST;
5070 v2s16 pos = sector->getPos();
5071 createDir(m_savedir);
5072 createDir(m_savedir+"/sectors");
5073 std::string dir = getSectorDir(pos);
5076 std::string fullpath = dir + "/heightmap";
5077 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5078 if(o.good() == false)
5079 throw FileNotGoodException("Cannot open master heightmap");
5081 sector->serialize(o, version);
5083 sector->differs_from_disk = false;
5086 MapSector* ServerMap::loadSectorMeta(std::string dirname)
5088 DSTACK(__FUNCTION_NAME);
5090 v2s16 p2d = getSectorPos(dirname);
5091 std::string dir = m_savedir + "/sectors/" + dirname;
5093 std::string fullpath = dir + "/heightmap";
5094 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5095 if(is.good() == false)
5096 throw FileNotGoodException("Cannot open sector heightmap");
5098 ServerMapSector *sector = ServerMapSector::deSerialize
5099 (is, this, p2d, &m_hwrapper, m_sectors);
5101 sector->differs_from_disk = false;
5106 bool ServerMap::loadSectorFull(v2s16 p2d)
5108 DSTACK(__FUNCTION_NAME);
5109 std::string sectorsubdir = getSectorSubDir(p2d);
5111 MapSector *sector = NULL;
5113 JMutexAutoLock lock(m_sector_mutex);
5116 sector = loadSectorMeta(sectorsubdir);
5118 catch(InvalidFilenameException &e)
5122 catch(FileNotGoodException &e)
5126 catch(std::exception &e)
5131 if(ENABLE_BLOCK_LOADING)
5133 std::vector<fs::DirListNode> list2 = fs::GetDirListing
5134 (m_savedir+"/sectors/"+sectorsubdir);
5135 std::vector<fs::DirListNode>::iterator i2;
5136 for(i2=list2.begin(); i2!=list2.end(); i2++)
5142 loadBlock(sectorsubdir, i2->name, sector);
5144 catch(InvalidFilenameException &e)
5146 // This catches unknown crap in directory
5154 bool ServerMap::deFlushSector(v2s16 p2d)
5156 DSTACK(__FUNCTION_NAME);
5157 // See if it already exists in memory
5159 MapSector *sector = getSectorNoGenerate(p2d);
5162 catch(InvalidPositionException &e)
5165 Try to load the sector from disk.
5167 if(loadSectorFull(p2d) == true)
5176 void ServerMap::saveBlock(MapBlock *block)
5178 DSTACK(__FUNCTION_NAME);
5180 Dummy blocks are not written
5182 if(block->isDummy())
5184 /*v3s16 p = block->getPos();
5185 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
5186 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
5190 // Format used for writing
5191 u8 version = SER_FMT_VER_HIGHEST;
5193 v3s16 p3d = block->getPos();
5194 v2s16 p2d(p3d.X, p3d.Z);
5195 createDir(m_savedir);
5196 createDir(m_savedir+"/sectors");
5197 std::string dir = getSectorDir(p2d);
5200 // Block file is map/sectors/xxxxxxxx/xxxx
5202 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
5203 std::string fullpath = dir + "/" + cc;
5204 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5205 if(o.good() == false)
5206 throw FileNotGoodException("Cannot open block data");
5209 [0] u8 serialization version
5212 o.write((char*)&version, 1);
5214 block->serialize(o, version);
5217 Versions up from 9 have block objects.
5221 block->serializeObjects(o, version);
5224 // We just wrote it to the disk
5225 block->resetChangedFlag();
5228 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
5230 DSTACK(__FUNCTION_NAME);
5234 // Block file is map/sectors/xxxxxxxx/xxxx
5235 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
5236 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5237 if(is.good() == false)
5238 throw FileNotGoodException("Cannot open block file");
5240 v3s16 p3d = getBlockPos(sectordir, blockfile);
5241 v2s16 p2d(p3d.X, p3d.Z);
5243 assert(sector->getPos() == p2d);
5245 u8 version = SER_FMT_VER_INVALID;
5246 is.read((char*)&version, 1);
5248 /*u32 block_size = MapBlock::serializedLength(version);
5249 SharedBuffer<u8> data(block_size);
5250 is.read((char*)*data, block_size);*/
5252 // This will always return a sector because we're the server
5253 //MapSector *sector = emergeSector(p2d);
5255 MapBlock *block = NULL;
5256 bool created_new = false;
5258 block = sector->getBlockNoCreate(p3d.Y);
5260 catch(InvalidPositionException &e)
5262 block = sector->createBlankBlockNoInsert(p3d.Y);
5266 // deserialize block data
5267 block->deSerialize(is, version);
5270 Versions up from 9 have block objects.
5274 block->updateObjects(is, version, NULL, 0);
5278 sector->insertBlock(block);
5281 Convert old formats to new and save
5284 // Save old format blocks in new format
5285 if(version < SER_FMT_VER_HIGHEST)
5290 // We just loaded it from the disk, so it's up-to-date.
5291 block->resetChangedFlag();
5294 catch(SerializationError &e)
5296 dstream<<"WARNING: Invalid block data on disk "
5297 "(SerializationError). Ignoring."
5302 // Gets from master heightmap
5303 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
5305 assert(m_heightmap != NULL);
5313 corners[0] = m_heightmap->getGroundHeight
5314 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
5315 corners[1] = m_heightmap->getGroundHeight
5316 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
5317 corners[2] = m_heightmap->getGroundHeight
5318 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
5319 corners[3] = m_heightmap->getGroundHeight
5320 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
5323 void ServerMap::PrintInfo(std::ostream &out)
5334 ClientMap::ClientMap(
5336 MapDrawControl &control,
5337 scene::ISceneNode* parent,
5338 scene::ISceneManager* mgr,
5342 scene::ISceneNode(parent, mgr, id),
5349 /*m_box = core::aabbox3d<f32>(0,0,0,
5350 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
5351 /*m_box = core::aabbox3d<f32>(0,0,0,
5352 map->getSizeNodes().X * BS,
5353 map->getSizeNodes().Y * BS,
5354 map->getSizeNodes().Z * BS);*/
5355 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
5356 BS*1000000,BS*1000000,BS*1000000);
5358 //setPosition(v3f(BS,BS,BS));
5361 ClientMap::~ClientMap()
5363 JMutexAutoLock lock(mesh_mutex);
5372 MapSector * ClientMap::emergeSector(v2s16 p2d)
5374 DSTACK(__FUNCTION_NAME);
5375 // Check that it doesn't exist already
5377 return getSectorNoGenerate(p2d);
5379 catch(InvalidPositionException &e)
5383 // Create a sector with no heightmaps
5384 ClientMapSector *sector = new ClientMapSector(this, p2d);
5387 JMutexAutoLock lock(m_sector_mutex);
5388 m_sectors.insert(p2d, sector);
5394 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
5396 DSTACK(__FUNCTION_NAME);
5397 ClientMapSector *sector = NULL;
5399 JMutexAutoLock lock(m_sector_mutex);
5401 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
5405 sector = (ClientMapSector*)n->getValue();
5406 assert(sector->getId() == MAPSECTOR_CLIENT);
5410 sector = new ClientMapSector(this, p2d);
5412 JMutexAutoLock lock(m_sector_mutex);
5413 m_sectors.insert(p2d, sector);
5417 sector->deSerialize(is);
5420 void ClientMap::OnRegisterSceneNode()
5424 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5425 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5428 ISceneNode::OnRegisterSceneNode();
5431 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5433 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5434 DSTACK(__FUNCTION_NAME);
5436 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5439 Get time for measuring timeout.
5441 Measuring time is very useful for long delays when the
5442 machine is swapping a lot.
5444 int time1 = time(0);
5446 u32 daynight_ratio = m_client->getDayNightRatio();
5448 m_camera_mutex.Lock();
5449 v3f camera_position = m_camera_position;
5450 v3f camera_direction = m_camera_direction;
5451 m_camera_mutex.Unlock();
5454 Get all blocks and draw all visible ones
5457 v3s16 cam_pos_nodes(
5458 camera_position.X / BS,
5459 camera_position.Y / BS,
5460 camera_position.Z / BS);
5462 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5464 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5465 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5467 // Take a fair amount as we will be dropping more out later
5469 p_nodes_min.X / MAP_BLOCKSIZE - 1,
5470 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
5471 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
5473 p_nodes_max.X / MAP_BLOCKSIZE + 1,
5474 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
5475 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
5477 u32 vertex_count = 0;
5479 // For limiting number of mesh updates per frame
5480 u32 mesh_update_count = 0;
5482 u32 blocks_would_have_drawn = 0;
5483 u32 blocks_drawn = 0;
5485 //NOTE: The sectors map should be locked but we're not doing it
5486 // because it'd cause too much delays
5488 int timecheck_counter = 0;
5489 core::map<v2s16, MapSector*>::Iterator si;
5490 si = m_sectors.getIterator();
5491 for(; si.atEnd() == false; si++)
5494 timecheck_counter++;
5495 if(timecheck_counter > 50)
5497 int time2 = time(0);
5498 if(time2 > time1 + 4)
5500 dstream<<"ClientMap::renderMap(): "
5501 "Rendering takes ages, returning."
5508 MapSector *sector = si.getNode()->getValue();
5509 v2s16 sp = sector->getPos();
5511 if(m_control.range_all == false)
5513 if(sp.X < p_blocks_min.X
5514 || sp.X > p_blocks_max.X
5515 || sp.Y < p_blocks_min.Z
5516 || sp.Y > p_blocks_max.Z)
5520 core::list< MapBlock * > sectorblocks;
5521 sector->getBlocks(sectorblocks);
5527 core::list< MapBlock * >::Iterator i;
5528 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5530 MapBlock *block = *i;
5533 Compare block position to camera position, skip
5534 if not seen on display
5537 float range = 100000 * BS;
5538 if(m_control.range_all == false)
5539 range = m_control.wanted_range * BS;
5542 if(isBlockInSight(block->getPos(), camera_position,
5543 camera_direction, range, &d) == false)
5549 v3s16 blockpos_nodes = block->getPosRelative();
5551 // Block center position
5553 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
5554 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
5555 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
5558 // Block position relative to camera
5559 v3f blockpos_relative = blockpos - camera_position;
5561 // Distance in camera direction (+=front, -=back)
5562 f32 dforward = blockpos_relative.dotProduct(camera_direction);
5565 f32 d = blockpos_relative.getLength();
5567 if(m_control.range_all == false)
5569 // If block is far away, don't draw it
5570 if(d > m_control.wanted_range * BS)
5574 // Maximum radius of a block
5575 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
5577 // If block is (nearly) touching the camera, don't
5578 // bother validating further (that is, render it anyway)
5579 if(d > block_max_radius * 1.5)
5581 // Cosine of the angle between the camera direction
5582 // and the block direction (camera_direction is an unit vector)
5583 f32 cosangle = dforward / d;
5585 // Compensate for the size of the block
5586 // (as the block has to be shown even if it's a bit off FOV)
5587 // This is an estimate.
5588 cosangle += block_max_radius / dforward;
5590 // If block is not in the field of view, skip it
5591 //if(cosangle < cos(FOV_ANGLE/2))
5592 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
5597 v3s16 blockpos_nodes = block->getPosRelative();
5599 // Block center position
5601 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
5602 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
5603 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
5606 // Block position relative to camera
5607 v3f blockpos_relative = blockpos - camera_position;
5610 f32 d = blockpos_relative.getLength();
5618 bool mesh_expired = false;
5621 JMutexAutoLock lock(block->mesh_mutex);
5623 mesh_expired = block->getMeshExpired();
5625 // Mesh has not been expired and there is no mesh:
5626 // block has no content
5627 if(block->mesh == NULL && mesh_expired == false)
5631 f32 faraway = BS*50;
5632 //f32 faraway = m_control.wanted_range * BS;
5635 This has to be done with the mesh_mutex unlocked
5637 // Pretty random but this should work somewhat nicely
5638 if(mesh_expired && (
5639 (mesh_update_count < 3
5640 && (d < faraway || mesh_update_count < 2)
5643 (m_control.range_all && mesh_update_count < 20)
5646 /*if(mesh_expired && mesh_update_count < 6
5647 && (d < faraway || mesh_update_count < 3))*/
5649 mesh_update_count++;
5651 // Mesh has been expired: generate new mesh
5652 //block->updateMeshes(daynight_i);
5653 block->updateMesh(daynight_ratio);
5655 mesh_expired = false;
5659 Don't draw an expired mesh that is far away
5661 /*if(mesh_expired && d >= faraway)
5664 // Instead, delete it
5665 JMutexAutoLock lock(block->mesh_mutex);
5668 block->mesh->drop();
5671 // And continue to next block
5676 Draw the faces of the block
5679 JMutexAutoLock lock(block->mesh_mutex);
5681 scene::SMesh *mesh = block->mesh;
5686 blocks_would_have_drawn++;
5687 if(blocks_drawn >= m_control.wanted_max_blocks
5688 && m_control.range_all == false
5689 && d > m_control.wanted_min_range * BS)
5693 u32 c = mesh->getMeshBufferCount();
5695 for(u32 i=0; i<c; i++)
5697 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
5698 const video::SMaterial& material = buf->getMaterial();
5699 video::IMaterialRenderer* rnd =
5700 driver->getMaterialRenderer(material.MaterialType);
5701 bool transparent = (rnd && rnd->isTransparent());
5702 // Render transparent on transparent pass and likewise.
5703 if(transparent == is_transparent_pass)
5705 driver->setMaterial(buf->getMaterial());
5706 driver->drawMeshBuffer(buf);
5707 vertex_count += buf->getVertexCount();
5711 } // foreach sectorblocks
5714 m_control.blocks_drawn = blocks_drawn;
5715 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
5717 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
5718 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
5721 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
5722 core::map<v3s16, MapBlock*> *affected_blocks)
5724 bool changed = false;
5726 Add it to all blocks touching it
5729 v3s16(0,0,0), // this
5730 v3s16(0,0,1), // back
5731 v3s16(0,1,0), // top
5732 v3s16(1,0,0), // right
5733 v3s16(0,0,-1), // front
5734 v3s16(0,-1,0), // bottom
5735 v3s16(-1,0,0), // left
5737 for(u16 i=0; i<7; i++)
5739 v3s16 p2 = p + dirs[i];
5740 // Block position of neighbor (or requested) node
5741 v3s16 blockpos = getNodeBlockPos(p2);
5742 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5743 if(blockref == NULL)
5745 // Relative position of requested node
5746 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5747 if(blockref->setTempMod(relpos, mod))
5752 if(changed && affected_blocks!=NULL)
5754 for(u16 i=0; i<7; i++)
5756 v3s16 p2 = p + dirs[i];
5757 // Block position of neighbor (or requested) node
5758 v3s16 blockpos = getNodeBlockPos(p2);
5759 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5760 if(blockref == NULL)
5762 affected_blocks->insert(blockpos, blockref);
5768 bool ClientMap::clearTempMod(v3s16 p,
5769 core::map<v3s16, MapBlock*> *affected_blocks)
5771 bool changed = false;
5773 v3s16(0,0,0), // this
5774 v3s16(0,0,1), // back
5775 v3s16(0,1,0), // top
5776 v3s16(1,0,0), // right
5777 v3s16(0,0,-1), // front
5778 v3s16(0,-1,0), // bottom
5779 v3s16(-1,0,0), // left
5781 for(u16 i=0; i<7; i++)
5783 v3s16 p2 = p + dirs[i];
5784 // Block position of neighbor (or requested) node
5785 v3s16 blockpos = getNodeBlockPos(p2);
5786 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5787 if(blockref == NULL)
5789 // Relative position of requested node
5790 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5791 if(blockref->clearTempMod(relpos))
5796 if(changed && affected_blocks!=NULL)
5798 for(u16 i=0; i<7; i++)
5800 v3s16 p2 = p + dirs[i];
5801 // Block position of neighbor (or requested) node
5802 v3s16 blockpos = getNodeBlockPos(p2);
5803 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5804 if(blockref == NULL)
5806 affected_blocks->insert(blockpos, blockref);
5812 void ClientMap::PrintInfo(std::ostream &out)
5823 MapVoxelManipulator::MapVoxelManipulator(Map *map)
5828 MapVoxelManipulator::~MapVoxelManipulator()
5830 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
5834 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5836 TimeTaker timer1("emerge", &emerge_time);
5838 // Units of these are MapBlocks
5839 v3s16 p_min = getNodeBlockPos(a.MinEdge);
5840 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
5842 VoxelArea block_area_nodes
5843 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5845 addArea(block_area_nodes);
5847 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5848 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5849 for(s32 x=p_min.X; x<=p_max.X; x++)
5852 core::map<v3s16, bool>::Node *n;
5853 n = m_loaded_blocks.find(p);
5857 bool block_data_inexistent = false;
5860 TimeTaker timer1("emerge load", &emerge_load_time);
5862 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
5863 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5866 dstream<<std::endl;*/
5868 MapBlock *block = m_map->getBlockNoCreate(p);
5869 if(block->isDummy())
5870 block_data_inexistent = true;
5872 block->copyTo(*this);
5874 catch(InvalidPositionException &e)
5876 block_data_inexistent = true;
5879 if(block_data_inexistent)
5881 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5882 // Fill with VOXELFLAG_INEXISTENT
5883 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5884 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5886 s32 i = m_area.index(a.MinEdge.X,y,z);
5887 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5891 m_loaded_blocks.insert(p, !block_data_inexistent);
5894 //dstream<<"emerge done"<<std::endl;
5898 SUGG: Add an option to only update eg. water and air nodes.
5899 This will make it interfere less with important stuff if
5902 void MapVoxelManipulator::blitBack
5903 (core::map<v3s16, MapBlock*> & modified_blocks)
5905 if(m_area.getExtent() == v3s16(0,0,0))
5908 //TimeTaker timer1("blitBack");
5910 /*dstream<<"blitBack(): m_loaded_blocks.size()="
5911 <<m_loaded_blocks.size()<<std::endl;*/
5914 Initialize block cache
5916 v3s16 blockpos_last;
5917 MapBlock *block = NULL;
5918 bool block_checked_in_modified = false;
5920 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
5921 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
5922 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
5926 u8 f = m_flags[m_area.index(p)];
5927 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
5930 MapNode &n = m_data[m_area.index(p)];
5932 v3s16 blockpos = getNodeBlockPos(p);
5937 if(block == NULL || blockpos != blockpos_last){
5938 block = m_map->getBlockNoCreate(blockpos);
5939 blockpos_last = blockpos;
5940 block_checked_in_modified = false;
5943 // Calculate relative position in block
5944 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
5946 // Don't continue if nothing has changed here
5947 if(block->getNode(relpos) == n)
5950 //m_map->setNode(m_area.MinEdge + p, n);
5951 block->setNode(relpos, n);
5954 Make sure block is in modified_blocks
5956 if(block_checked_in_modified == false)
5958 modified_blocks[blockpos] = block;
5959 block_checked_in_modified = true;
5962 catch(InvalidPositionException &e)
5968 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
5969 MapVoxelManipulator(map)
5973 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
5977 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5979 // Just create the area so that it can be pointed to
5980 VoxelManipulator::emerge(a, caller_id);
5983 void ManualMapVoxelManipulator::initialEmerge(
5984 v3s16 blockpos_min, v3s16 blockpos_max)
5986 TimeTaker timer1("initialEmerge", &emerge_time);
5988 // Units of these are MapBlocks
5989 v3s16 p_min = blockpos_min;
5990 v3s16 p_max = blockpos_max;
5992 VoxelArea block_area_nodes
5993 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5995 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
5998 dstream<<"initialEmerge: area: ";
5999 block_area_nodes.print(dstream);
6000 dstream<<" ("<<size_MB<<"MB)";
6004 addArea(block_area_nodes);
6006 for(s32 z=p_min.Z; z<=p_max.Z; z++)
6007 for(s32 y=p_min.Y; y<=p_max.Y; y++)
6008 for(s32 x=p_min.X; x<=p_max.X; x++)
6011 core::map<v3s16, bool>::Node *n;
6012 n = m_loaded_blocks.find(p);
6016 bool block_data_inexistent = false;
6019 TimeTaker timer1("emerge load", &emerge_load_time);
6021 MapBlock *block = m_map->getBlockNoCreate(p);
6022 if(block->isDummy())
6023 block_data_inexistent = true;
6025 block->copyTo(*this);
6027 catch(InvalidPositionException &e)
6029 block_data_inexistent = true;
6032 if(block_data_inexistent)
6035 Mark area inexistent
6037 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6038 // Fill with VOXELFLAG_INEXISTENT
6039 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
6040 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
6042 s32 i = m_area.index(a.MinEdge.X,y,z);
6043 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
6047 m_loaded_blocks.insert(p, !block_data_inexistent);
6051 void ManualMapVoxelManipulator::blitBackAll(
6052 core::map<v3s16, MapBlock*> * modified_blocks)
6054 if(m_area.getExtent() == v3s16(0,0,0))
6058 Copy data of all blocks
6060 for(core::map<v3s16, bool>::Iterator
6061 i = m_loaded_blocks.getIterator();
6062 i.atEnd() == false; i++)
6064 bool existed = i.getNode()->getValue();
6065 if(existed == false)
6067 v3s16 p = i.getNode()->getKey();
6068 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
6071 dstream<<"WARNING: "<<__FUNCTION_NAME
6072 <<": got NULL block "
6073 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
6078 block->copyFrom(*this);
6081 modified_blocks->insert(p, block);