3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "jmutexautolock.h"
30 #include "serverobject.h"
31 #include "content_mapnode.h"
37 Map::Map(std::ostream &dout):
41 /*m_sector_mutex.Init();
42 assert(m_sector_mutex.IsInitialized());*/
50 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
51 for(; i.atEnd() == false; i++)
53 MapSector *sector = i.getNode()->getValue();
58 void Map::addEventReceiver(MapEventReceiver *event_receiver)
60 m_event_receivers.insert(event_receiver, false);
63 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
65 if(m_event_receivers.find(event_receiver) == NULL)
67 m_event_receivers.remove(event_receiver);
70 void Map::dispatchEvent(MapEditEvent *event)
72 for(core::map<MapEventReceiver*, bool>::Iterator
73 i = m_event_receivers.getIterator();
74 i.atEnd()==false; i++)
76 MapEventReceiver* event_receiver = i.getNode()->getKey();
77 event_receiver->onMapEditEvent(event);
81 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
83 if(m_sector_cache != NULL && p == m_sector_cache_p){
84 MapSector * sector = m_sector_cache;
85 // Reset inactivity timer
86 sector->usage_timer = 0.0;
90 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
95 MapSector *sector = n->getValue();
97 // Cache the last result
99 m_sector_cache = sector;
101 // Reset inactivity timer
102 sector->usage_timer = 0.0;
106 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
108 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
110 return getSectorNoGenerateNoExNoLock(p);
113 MapSector * Map::getSectorNoGenerate(v2s16 p)
115 MapSector *sector = getSectorNoGenerateNoEx(p);
117 throw InvalidPositionException();
122 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
124 v2s16 p2d(p3d.X, p3d.Z);
125 MapSector * sector = getSectorNoGenerate(p2d);
127 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
132 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
136 v2s16 p2d(p3d.X, p3d.Z);
137 MapSector * sector = getSectorNoGenerate(p2d);
138 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
141 catch(InvalidPositionException &e)
147 /*MapBlock * Map::getBlockCreate(v3s16 p3d)
149 v2s16 p2d(p3d.X, p3d.Z);
150 MapSector * sector = getSectorCreate(p2d);
152 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
155 block = sector->createBlankBlock(p3d.Y);
159 bool Map::isNodeUnderground(v3s16 p)
161 v3s16 blockpos = getNodeBlockPos(p);
163 MapBlock * block = getBlockNoCreate(blockpos);
164 return block->getIsUnderground();
166 catch(InvalidPositionException &e)
173 Goes recursively through the neighbours of the node.
175 Alters only transparent nodes.
177 If the lighting of the neighbour is lower than the lighting of
178 the node was (before changing it to 0 at the step before), the
179 lighting of the neighbour is set to 0 and then the same stuff
180 repeats for the neighbour.
182 The ending nodes of the routine are stored in light_sources.
183 This is useful when a light is removed. In such case, this
184 routine can be called for the light node and then again for
185 light_sources to re-light the area without the removed light.
187 values of from_nodes are lighting values.
189 void Map::unspreadLight(enum LightBank bank,
190 core::map<v3s16, u8> & from_nodes,
191 core::map<v3s16, bool> & light_sources,
192 core::map<v3s16, MapBlock*> & modified_blocks)
195 v3s16(0,0,1), // back
197 v3s16(1,0,0), // right
198 v3s16(0,0,-1), // front
199 v3s16(0,-1,0), // bottom
200 v3s16(-1,0,0), // left
203 if(from_nodes.size() == 0)
206 u32 blockchangecount = 0;
208 core::map<v3s16, u8> unlighted_nodes;
209 core::map<v3s16, u8>::Iterator j;
210 j = from_nodes.getIterator();
213 Initialize block cache
216 MapBlock *block = NULL;
217 // Cache this a bit, too
218 bool block_checked_in_modified = false;
220 for(; j.atEnd() == false; j++)
222 v3s16 pos = j.getNode()->getKey();
223 v3s16 blockpos = getNodeBlockPos(pos);
225 // Only fetch a new block if the block position has changed
227 if(block == NULL || blockpos != blockpos_last){
228 block = getBlockNoCreate(blockpos);
229 blockpos_last = blockpos;
231 block_checked_in_modified = false;
235 catch(InvalidPositionException &e)
243 // Calculate relative position in block
244 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
246 // Get node straight from the block
247 MapNode n = block->getNode(relpos);
249 u8 oldlight = j.getNode()->getValue();
251 // Loop through 6 neighbors
252 for(u16 i=0; i<6; i++)
254 // Get the position of the neighbor node
255 v3s16 n2pos = pos + dirs[i];
257 // Get the block where the node is located
258 v3s16 blockpos = getNodeBlockPos(n2pos);
262 // Only fetch a new block if the block position has changed
264 if(block == NULL || blockpos != blockpos_last){
265 block = getBlockNoCreate(blockpos);
266 blockpos_last = blockpos;
268 block_checked_in_modified = false;
272 catch(InvalidPositionException &e)
277 // Calculate relative position in block
278 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
279 // Get node straight from the block
280 MapNode n2 = block->getNode(relpos);
282 bool changed = false;
284 //TODO: Optimize output by optimizing light_sources?
287 If the neighbor is dimmer than what was specified
288 as oldlight (the light of the previous node)
290 if(n2.getLight(bank) < oldlight)
293 And the neighbor is transparent and it has some light
295 if(n2.light_propagates() && n2.getLight(bank) != 0)
298 Set light to 0 and add to queue
301 u8 current_light = n2.getLight(bank);
302 n2.setLight(bank, 0);
303 block->setNode(relpos, n2);
305 unlighted_nodes.insert(n2pos, current_light);
309 Remove from light_sources if it is there
310 NOTE: This doesn't happen nearly at all
312 /*if(light_sources.find(n2pos))
314 std::cout<<"Removed from light_sources"<<std::endl;
315 light_sources.remove(n2pos);
320 if(light_sources.find(n2pos) != NULL)
321 light_sources.remove(n2pos);*/
324 light_sources.insert(n2pos, true);
327 // Add to modified_blocks
328 if(changed == true && block_checked_in_modified == false)
330 // If the block is not found in modified_blocks, add.
331 if(modified_blocks.find(blockpos) == NULL)
333 modified_blocks.insert(blockpos, block);
335 block_checked_in_modified = true;
338 catch(InvalidPositionException &e)
345 /*dstream<<"unspreadLight(): Changed block "
346 <<blockchangecount<<" times"
347 <<" for "<<from_nodes.size()<<" nodes"
350 if(unlighted_nodes.size() > 0)
351 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
355 A single-node wrapper of the above
357 void Map::unLightNeighbors(enum LightBank bank,
358 v3s16 pos, u8 lightwas,
359 core::map<v3s16, bool> & light_sources,
360 core::map<v3s16, MapBlock*> & modified_blocks)
362 core::map<v3s16, u8> from_nodes;
363 from_nodes.insert(pos, lightwas);
365 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
369 Lights neighbors of from_nodes, collects all them and then
372 void Map::spreadLight(enum LightBank bank,
373 core::map<v3s16, bool> & from_nodes,
374 core::map<v3s16, MapBlock*> & modified_blocks)
376 const v3s16 dirs[6] = {
377 v3s16(0,0,1), // back
379 v3s16(1,0,0), // right
380 v3s16(0,0,-1), // front
381 v3s16(0,-1,0), // bottom
382 v3s16(-1,0,0), // left
385 if(from_nodes.size() == 0)
388 u32 blockchangecount = 0;
390 core::map<v3s16, bool> lighted_nodes;
391 core::map<v3s16, bool>::Iterator j;
392 j = from_nodes.getIterator();
395 Initialize block cache
398 MapBlock *block = NULL;
399 // Cache this a bit, too
400 bool block_checked_in_modified = false;
402 for(; j.atEnd() == false; j++)
403 //for(; j != from_nodes.end(); j++)
405 v3s16 pos = j.getNode()->getKey();
407 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
408 v3s16 blockpos = getNodeBlockPos(pos);
410 // Only fetch a new block if the block position has changed
412 if(block == NULL || blockpos != blockpos_last){
413 block = getBlockNoCreate(blockpos);
414 blockpos_last = blockpos;
416 block_checked_in_modified = false;
420 catch(InvalidPositionException &e)
428 // Calculate relative position in block
429 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
431 // Get node straight from the block
432 MapNode n = block->getNode(relpos);
434 u8 oldlight = n.getLight(bank);
435 u8 newlight = diminish_light(oldlight);
437 // Loop through 6 neighbors
438 for(u16 i=0; i<6; i++){
439 // Get the position of the neighbor node
440 v3s16 n2pos = pos + dirs[i];
442 // Get the block where the node is located
443 v3s16 blockpos = getNodeBlockPos(n2pos);
447 // Only fetch a new block if the block position has changed
449 if(block == NULL || blockpos != blockpos_last){
450 block = getBlockNoCreate(blockpos);
451 blockpos_last = blockpos;
453 block_checked_in_modified = false;
457 catch(InvalidPositionException &e)
462 // Calculate relative position in block
463 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
464 // Get node straight from the block
465 MapNode n2 = block->getNode(relpos);
467 bool changed = false;
469 If the neighbor is brighter than the current node,
470 add to list (it will light up this node on its turn)
472 if(n2.getLight(bank) > undiminish_light(oldlight))
474 lighted_nodes.insert(n2pos, true);
475 //lighted_nodes.push_back(n2pos);
479 If the neighbor is dimmer than how much light this node
480 would spread on it, add to list
482 if(n2.getLight(bank) < newlight)
484 if(n2.light_propagates())
486 n2.setLight(bank, newlight);
487 block->setNode(relpos, n2);
488 lighted_nodes.insert(n2pos, true);
489 //lighted_nodes.push_back(n2pos);
494 // Add to modified_blocks
495 if(changed == true && block_checked_in_modified == false)
497 // If the block is not found in modified_blocks, add.
498 if(modified_blocks.find(blockpos) == NULL)
500 modified_blocks.insert(blockpos, block);
502 block_checked_in_modified = true;
505 catch(InvalidPositionException &e)
512 /*dstream<<"spreadLight(): Changed block "
513 <<blockchangecount<<" times"
514 <<" for "<<from_nodes.size()<<" nodes"
517 if(lighted_nodes.size() > 0)
518 spreadLight(bank, lighted_nodes, modified_blocks);
522 A single-node source variation of the above.
524 void Map::lightNeighbors(enum LightBank bank,
526 core::map<v3s16, MapBlock*> & modified_blocks)
528 core::map<v3s16, bool> from_nodes;
529 from_nodes.insert(pos, true);
530 spreadLight(bank, from_nodes, modified_blocks);
533 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
536 v3s16(0,0,1), // back
538 v3s16(1,0,0), // right
539 v3s16(0,0,-1), // front
540 v3s16(0,-1,0), // bottom
541 v3s16(-1,0,0), // left
544 u8 brightest_light = 0;
545 v3s16 brightest_pos(0,0,0);
546 bool found_something = false;
548 // Loop through 6 neighbors
549 for(u16 i=0; i<6; i++){
550 // Get the position of the neighbor node
551 v3s16 n2pos = p + dirs[i];
556 catch(InvalidPositionException &e)
560 if(n2.getLight(bank) > brightest_light || found_something == false){
561 brightest_light = n2.getLight(bank);
562 brightest_pos = n2pos;
563 found_something = true;
567 if(found_something == false)
568 throw InvalidPositionException();
570 return brightest_pos;
574 Propagates sunlight down from a node.
575 Starting point gets sunlight.
577 Returns the lowest y value of where the sunlight went.
579 Mud is turned into grass in where the sunlight stops.
581 s16 Map::propagateSunlight(v3s16 start,
582 core::map<v3s16, MapBlock*> & modified_blocks)
587 v3s16 pos(start.X, y, start.Z);
589 v3s16 blockpos = getNodeBlockPos(pos);
592 block = getBlockNoCreate(blockpos);
594 catch(InvalidPositionException &e)
599 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
600 MapNode n = block->getNode(relpos);
602 if(n.sunlight_propagates())
604 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
605 block->setNode(relpos, n);
607 modified_blocks.insert(blockpos, block);
611 /*// Turn mud into grass
612 if(n.d == CONTENT_MUD)
615 block->setNode(relpos, n);
616 modified_blocks.insert(blockpos, block);
619 // Sunlight goes no further
626 void Map::updateLighting(enum LightBank bank,
627 core::map<v3s16, MapBlock*> & a_blocks,
628 core::map<v3s16, MapBlock*> & modified_blocks)
630 /*m_dout<<DTIME<<"Map::updateLighting(): "
631 <<a_blocks.size()<<" blocks."<<std::endl;*/
633 //TimeTaker timer("updateLighting");
637 //u32 count_was = modified_blocks.size();
639 core::map<v3s16, MapBlock*> blocks_to_update;
641 core::map<v3s16, bool> light_sources;
643 core::map<v3s16, u8> unlight_from;
645 core::map<v3s16, MapBlock*>::Iterator i;
646 i = a_blocks.getIterator();
647 for(; i.atEnd() == false; i++)
649 MapBlock *block = i.getNode()->getValue();
653 // Don't bother with dummy blocks.
657 v3s16 pos = block->getPos();
658 modified_blocks.insert(pos, block);
660 blocks_to_update.insert(pos, block);
663 Clear all light from block
665 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
666 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
667 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
672 MapNode n = block->getNode(v3s16(x,y,z));
673 u8 oldlight = n.getLight(bank);
675 block->setNode(v3s16(x,y,z), n);
677 // Collect borders for unlighting
678 if(x==0 || x == MAP_BLOCKSIZE-1
679 || y==0 || y == MAP_BLOCKSIZE-1
680 || z==0 || z == MAP_BLOCKSIZE-1)
682 v3s16 p_map = p + v3s16(
685 MAP_BLOCKSIZE*pos.Z);
686 unlight_from.insert(p_map, oldlight);
689 catch(InvalidPositionException &e)
692 This would happen when dealing with a
696 dstream<<"updateLighting(): InvalidPositionException"
701 if(bank == LIGHTBANK_DAY)
703 bool bottom_valid = block->propagateSunlight(light_sources);
705 // If bottom is valid, we're done.
709 else if(bank == LIGHTBANK_NIGHT)
711 // For night lighting, sunlight is not propagated
716 // Invalid lighting bank
720 /*dstream<<"Bottom for sunlight-propagated block ("
721 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
724 // Bottom sunlight is not valid; get the block and loop to it
728 block = getBlockNoCreate(pos);
730 catch(InvalidPositionException &e)
740 TimeTaker timer("unspreadLight");
741 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
746 u32 diff = modified_blocks.size() - count_was;
747 count_was = modified_blocks.size();
748 dstream<<"unspreadLight modified "<<diff<<std::endl;
752 TimeTaker timer("spreadLight");
753 spreadLight(bank, light_sources, modified_blocks);
758 u32 diff = modified_blocks.size() - count_was;
759 count_was = modified_blocks.size();
760 dstream<<"spreadLight modified "<<diff<<std::endl;
765 //MapVoxelManipulator vmanip(this);
767 // Make a manual voxel manipulator and load all the blocks
768 // that touch the requested blocks
769 ManualMapVoxelManipulator vmanip(this);
770 core::map<v3s16, MapBlock*>::Iterator i;
771 i = blocks_to_update.getIterator();
772 for(; i.atEnd() == false; i++)
774 MapBlock *block = i.getNode()->getValue();
775 v3s16 p = block->getPos();
777 // Add all surrounding blocks
778 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
781 Add all surrounding blocks that have up-to-date lighting
782 NOTE: This doesn't quite do the job (not everything
783 appropriate is lighted)
785 /*for(s16 z=-1; z<=1; z++)
786 for(s16 y=-1; y<=1; y++)
787 for(s16 x=-1; x<=1; x++)
790 MapBlock *block = getBlockNoCreateNoEx(p);
795 if(block->getLightingExpired())
797 vmanip.initialEmerge(p, p);
800 // Lighting of block will be updated completely
801 block->setLightingExpired(false);
805 //TimeTaker timer("unSpreadLight");
806 vmanip.unspreadLight(bank, unlight_from, light_sources);
809 //TimeTaker timer("spreadLight");
810 vmanip.spreadLight(bank, light_sources);
813 //TimeTaker timer("blitBack");
814 vmanip.blitBack(modified_blocks);
816 /*dstream<<"emerge_time="<<emerge_time<<std::endl;
820 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
823 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
824 core::map<v3s16, MapBlock*> & modified_blocks)
826 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
827 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
830 Update information about whether day and night light differ
832 for(core::map<v3s16, MapBlock*>::Iterator
833 i = modified_blocks.getIterator();
834 i.atEnd() == false; i++)
836 MapBlock *block = i.getNode()->getValue();
837 block->updateDayNightDiff();
843 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
844 core::map<v3s16, MapBlock*> &modified_blocks)
847 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
848 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
851 From this node to nodes underneath:
852 If lighting is sunlight (1.0), unlight neighbours and
857 v3s16 toppos = p + v3s16(0,1,0);
858 v3s16 bottompos = p + v3s16(0,-1,0);
860 bool node_under_sunlight = true;
861 core::map<v3s16, bool> light_sources;
864 If there is a node at top and it doesn't have sunlight,
865 there has not been any sunlight going down.
867 Otherwise there probably is.
870 MapNode topnode = getNode(toppos);
872 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
873 node_under_sunlight = false;
875 catch(InvalidPositionException &e)
881 If the new node is solid and there is grass below, change it to mud
883 if(content_features(n.d).walkable == true)
886 MapNode bottomnode = getNode(bottompos);
888 if(bottomnode.d == CONTENT_GRASS
889 || bottomnode.d == CONTENT_GRASS_FOOTSTEPS)
891 bottomnode.d = CONTENT_MUD;
892 setNode(bottompos, bottomnode);
895 catch(InvalidPositionException &e)
903 If the new node is mud and it is under sunlight, change it
906 if(n.d == CONTENT_MUD && node_under_sunlight)
913 Remove all light that has come out of this node
916 enum LightBank banks[] =
921 for(s32 i=0; i<2; i++)
923 enum LightBank bank = banks[i];
925 u8 lightwas = getNode(p).getLight(bank);
927 // Add the block of the added node to modified_blocks
928 v3s16 blockpos = getNodeBlockPos(p);
929 MapBlock * block = getBlockNoCreate(blockpos);
930 assert(block != NULL);
931 modified_blocks.insert(blockpos, block);
933 assert(isValidPosition(p));
935 // Unlight neighbours of node.
936 // This means setting light of all consequent dimmer nodes
938 // This also collects the nodes at the border which will spread
939 // light again into this.
940 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
946 If node lets sunlight through and is under sunlight, it has
949 if(node_under_sunlight && content_features(n.d).sunlight_propagates)
951 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
955 Set the node on the map
964 NodeMetadata *meta_proto = content_features(n.d).initial_metadata;
967 NodeMetadata *meta = meta_proto->clone();
968 setNodeMetadata(p, meta);
972 If node is under sunlight and doesn't let sunlight through,
973 take all sunlighted nodes under it and clear light from them
974 and from where the light has been spread.
975 TODO: This could be optimized by mass-unlighting instead
978 if(node_under_sunlight && !content_features(n.d).sunlight_propagates)
982 //m_dout<<DTIME<<"y="<<y<<std::endl;
983 v3s16 n2pos(p.X, y, p.Z);
989 catch(InvalidPositionException &e)
994 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
996 unLightNeighbors(LIGHTBANK_DAY,
997 n2pos, n2.getLight(LIGHTBANK_DAY),
998 light_sources, modified_blocks);
999 n2.setLight(LIGHTBANK_DAY, 0);
1007 for(s32 i=0; i<2; i++)
1009 enum LightBank bank = banks[i];
1012 Spread light from all nodes that might be capable of doing so
1014 spreadLight(bank, light_sources, modified_blocks);
1018 Update information about whether day and night light differ
1020 for(core::map<v3s16, MapBlock*>::Iterator
1021 i = modified_blocks.getIterator();
1022 i.atEnd() == false; i++)
1024 MapBlock *block = i.getNode()->getValue();
1025 block->updateDayNightDiff();
1029 Add neighboring liquid nodes and the node itself if it is
1030 liquid (=water node was added) to transform queue.
1033 v3s16(0,0,0), // self
1034 v3s16(0,0,1), // back
1035 v3s16(0,1,0), // top
1036 v3s16(1,0,0), // right
1037 v3s16(0,0,-1), // front
1038 v3s16(0,-1,0), // bottom
1039 v3s16(-1,0,0), // left
1041 for(u16 i=0; i<7; i++)
1046 v3s16 p2 = p + dirs[i];
1048 MapNode n2 = getNode(p2);
1049 if(content_liquid(n2.d))
1051 m_transforming_liquid.push_back(p2);
1054 }catch(InvalidPositionException &e)
1062 void Map::removeNodeAndUpdate(v3s16 p,
1063 core::map<v3s16, MapBlock*> &modified_blocks)
1065 /*PrintInfo(m_dout);
1066 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1067 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1069 bool node_under_sunlight = true;
1071 v3s16 toppos = p + v3s16(0,1,0);
1073 // Node will be replaced with this
1074 u8 replace_material = CONTENT_AIR;
1077 If there is a node at top and it doesn't have sunlight,
1078 there will be no sunlight going down.
1081 MapNode topnode = getNode(toppos);
1083 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1084 node_under_sunlight = false;
1086 catch(InvalidPositionException &e)
1090 core::map<v3s16, bool> light_sources;
1092 enum LightBank banks[] =
1097 for(s32 i=0; i<2; i++)
1099 enum LightBank bank = banks[i];
1102 Unlight neighbors (in case the node is a light source)
1104 unLightNeighbors(bank, p,
1105 getNode(p).getLight(bank),
1106 light_sources, modified_blocks);
1110 Remove node metadata
1113 removeNodeMetadata(p);
1117 This also clears the lighting.
1121 n.d = replace_material;
1124 for(s32 i=0; i<2; i++)
1126 enum LightBank bank = banks[i];
1129 Recalculate lighting
1131 spreadLight(bank, light_sources, modified_blocks);
1134 // Add the block of the removed node to modified_blocks
1135 v3s16 blockpos = getNodeBlockPos(p);
1136 MapBlock * block = getBlockNoCreate(blockpos);
1137 assert(block != NULL);
1138 modified_blocks.insert(blockpos, block);
1141 If the removed node was under sunlight, propagate the
1142 sunlight down from it and then light all neighbors
1143 of the propagated blocks.
1145 if(node_under_sunlight)
1147 s16 ybottom = propagateSunlight(p, modified_blocks);
1148 /*m_dout<<DTIME<<"Node was under sunlight. "
1149 "Propagating sunlight";
1150 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1152 for(; y >= ybottom; y--)
1154 v3s16 p2(p.X, y, p.Z);
1155 /*m_dout<<DTIME<<"lighting neighbors of node ("
1156 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1158 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1163 // Set the lighting of this node to 0
1164 // TODO: Is this needed? Lighting is cleared up there already.
1166 MapNode n = getNode(p);
1167 n.setLight(LIGHTBANK_DAY, 0);
1170 catch(InvalidPositionException &e)
1176 for(s32 i=0; i<2; i++)
1178 enum LightBank bank = banks[i];
1180 // Get the brightest neighbour node and propagate light from it
1181 v3s16 n2p = getBrightestNeighbour(bank, p);
1183 MapNode n2 = getNode(n2p);
1184 lightNeighbors(bank, n2p, modified_blocks);
1186 catch(InvalidPositionException &e)
1192 Update information about whether day and night light differ
1194 for(core::map<v3s16, MapBlock*>::Iterator
1195 i = modified_blocks.getIterator();
1196 i.atEnd() == false; i++)
1198 MapBlock *block = i.getNode()->getValue();
1199 block->updateDayNightDiff();
1203 Add neighboring liquid nodes to transform queue.
1206 v3s16(0,0,1), // back
1207 v3s16(0,1,0), // top
1208 v3s16(1,0,0), // right
1209 v3s16(0,0,-1), // front
1210 v3s16(0,-1,0), // bottom
1211 v3s16(-1,0,0), // left
1213 for(u16 i=0; i<6; i++)
1218 v3s16 p2 = p + dirs[i];
1220 MapNode n2 = getNode(p2);
1221 if(content_liquid(n2.d))
1223 m_transforming_liquid.push_back(p2);
1226 }catch(InvalidPositionException &e)
1232 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1235 event.type = MEET_ADDNODE;
1239 bool succeeded = true;
1241 core::map<v3s16, MapBlock*> modified_blocks;
1242 addNodeAndUpdate(p, n, modified_blocks);
1244 // Copy modified_blocks to event
1245 for(core::map<v3s16, MapBlock*>::Iterator
1246 i = modified_blocks.getIterator();
1247 i.atEnd()==false; i++)
1249 event.modified_blocks.insert(i.getNode()->getKey(), false);
1252 catch(InvalidPositionException &e){
1256 dispatchEvent(&event);
1261 bool Map::removeNodeWithEvent(v3s16 p)
1264 event.type = MEET_REMOVENODE;
1267 bool succeeded = true;
1269 core::map<v3s16, MapBlock*> modified_blocks;
1270 removeNodeAndUpdate(p, modified_blocks);
1272 // Copy modified_blocks to event
1273 for(core::map<v3s16, MapBlock*>::Iterator
1274 i = modified_blocks.getIterator();
1275 i.atEnd()==false; i++)
1277 event.modified_blocks.insert(i.getNode()->getKey(), false);
1280 catch(InvalidPositionException &e){
1284 dispatchEvent(&event);
1289 bool Map::dayNightDiffed(v3s16 blockpos)
1292 v3s16 p = blockpos + v3s16(0,0,0);
1293 MapBlock *b = getBlockNoCreate(p);
1294 if(b->dayNightDiffed())
1297 catch(InvalidPositionException &e){}
1300 v3s16 p = blockpos + v3s16(-1,0,0);
1301 MapBlock *b = getBlockNoCreate(p);
1302 if(b->dayNightDiffed())
1305 catch(InvalidPositionException &e){}
1307 v3s16 p = blockpos + v3s16(0,-1,0);
1308 MapBlock *b = getBlockNoCreate(p);
1309 if(b->dayNightDiffed())
1312 catch(InvalidPositionException &e){}
1314 v3s16 p = blockpos + v3s16(0,0,-1);
1315 MapBlock *b = getBlockNoCreate(p);
1316 if(b->dayNightDiffed())
1319 catch(InvalidPositionException &e){}
1322 v3s16 p = blockpos + v3s16(1,0,0);
1323 MapBlock *b = getBlockNoCreate(p);
1324 if(b->dayNightDiffed())
1327 catch(InvalidPositionException &e){}
1329 v3s16 p = blockpos + v3s16(0,1,0);
1330 MapBlock *b = getBlockNoCreate(p);
1331 if(b->dayNightDiffed())
1334 catch(InvalidPositionException &e){}
1336 v3s16 p = blockpos + v3s16(0,0,1);
1337 MapBlock *b = getBlockNoCreate(p);
1338 if(b->dayNightDiffed())
1341 catch(InvalidPositionException &e){}
1347 Updates usage timers
1349 void Map::timerUpdate(float dtime)
1351 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
1353 core::map<v2s16, MapSector*>::Iterator si;
1355 si = m_sectors.getIterator();
1356 for(; si.atEnd() == false; si++)
1358 MapSector *sector = si.getNode()->getValue();
1359 sector->usage_timer += dtime;
1363 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1366 Wait for caches to be removed before continuing.
1368 This disables the existence of caches while locked
1370 //SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
1372 core::list<v2s16>::Iterator j;
1373 for(j=list.begin(); j!=list.end(); j++)
1375 MapSector *sector = m_sectors[*j];
1378 sector->deleteBlocks();
1383 If sector is in sector cache, remove it from there
1385 if(m_sector_cache == sector)
1387 m_sector_cache = NULL;
1390 Remove from map and delete
1392 m_sectors.remove(*j);
1398 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1399 core::list<v3s16> *deleted_blocks)
1401 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
1403 core::list<v2s16> sector_deletion_queue;
1404 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1405 for(; i.atEnd() == false; i++)
1407 MapSector *sector = i.getNode()->getValue();
1409 Delete sector from memory if it hasn't been used in a long time
1411 if(sector->usage_timer > timeout)
1413 sector_deletion_queue.push_back(i.getNode()->getKey());
1415 if(deleted_blocks != NULL)
1417 // Collect positions of blocks of sector
1418 MapSector *sector = i.getNode()->getValue();
1419 core::list<MapBlock*> blocks;
1420 sector->getBlocks(blocks);
1421 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1422 i != blocks.end(); i++)
1424 deleted_blocks->push_back((*i)->getPos());
1429 deleteSectors(sector_deletion_queue, only_blocks);
1430 return sector_deletion_queue.getSize();
1433 void Map::PrintInfo(std::ostream &out)
1438 #define WATER_DROP_BOOST 4
1440 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1442 DSTACK(__FUNCTION_NAME);
1443 //TimeTaker timer("transformLiquids()");
1446 u32 initial_size = m_transforming_liquid.size();
1448 /*if(initial_size != 0)
1449 dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1451 while(m_transforming_liquid.size() != 0)
1454 Get a queued transforming liquid node
1456 v3s16 p0 = m_transforming_liquid.pop_front();
1458 MapNode n0 = getNode(p0);
1460 // Don't deal with non-liquids
1461 if(content_liquid(n0.d) == false)
1464 bool is_source = !content_flowing_liquid(n0.d);
1466 u8 liquid_level = 8;
1467 if(is_source == false)
1468 liquid_level = n0.param2 & 0x0f;
1470 // Turn possible source into non-source
1471 u8 nonsource_c = make_liquid_flowing(n0.d);
1474 If not source, check that some node flows into this one
1475 and what is the level of liquid in this one
1477 if(is_source == false)
1479 s8 new_liquid_level_max = -1;
1481 v3s16 dirs_from[5] = {
1482 v3s16(0,1,0), // top
1483 v3s16(0,0,1), // back
1484 v3s16(1,0,0), // right
1485 v3s16(0,0,-1), // front
1486 v3s16(-1,0,0), // left
1488 for(u16 i=0; i<5; i++)
1493 bool from_top = (i==0);
1495 v3s16 p2 = p0 + dirs_from[i];
1496 MapNode n2 = getNode(p2);
1498 if(content_liquid(n2.d))
1500 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1501 // Check that the liquids are the same type
1502 if(n2_nonsource_c != nonsource_c)
1504 dstream<<"WARNING: Not handling: different liquids"
1505 " collide"<<std::endl;
1508 bool n2_is_source = !content_flowing_liquid(n2.d);
1509 s8 n2_liquid_level = 8;
1510 if(n2_is_source == false)
1511 n2_liquid_level = n2.param2 & 0x07;
1513 s8 new_liquid_level = -1;
1516 //new_liquid_level = 7;
1517 if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
1518 new_liquid_level = 7;
1520 new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
1522 else if(n2_liquid_level > 0)
1524 new_liquid_level = n2_liquid_level - 1;
1527 if(new_liquid_level > new_liquid_level_max)
1528 new_liquid_level_max = new_liquid_level;
1531 }catch(InvalidPositionException &e)
1537 If liquid level should be something else, update it and
1538 add all the neighboring water nodes to the transform queue.
1540 if(new_liquid_level_max != liquid_level)
1542 if(new_liquid_level_max == -1)
1544 // Remove water alltoghether
1551 n0.param2 = new_liquid_level_max;
1555 // Block has been modified
1557 v3s16 blockpos = getNodeBlockPos(p0);
1558 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1560 modified_blocks.insert(blockpos, block);
1564 Add neighboring non-source liquid nodes to transform queue.
1567 v3s16(0,0,1), // back
1568 v3s16(0,1,0), // top
1569 v3s16(1,0,0), // right
1570 v3s16(0,0,-1), // front
1571 v3s16(0,-1,0), // bottom
1572 v3s16(-1,0,0), // left
1574 for(u16 i=0; i<6; i++)
1579 v3s16 p2 = p0 + dirs[i];
1581 MapNode n2 = getNode(p2);
1582 if(content_flowing_liquid(n2.d))
1584 m_transforming_liquid.push_back(p2);
1587 }catch(InvalidPositionException &e)
1594 // Get a new one from queue if the node has turned into non-water
1595 if(content_liquid(n0.d) == false)
1599 Flow water from this node
1601 v3s16 dirs_to[5] = {
1602 v3s16(0,-1,0), // bottom
1603 v3s16(0,0,1), // back
1604 v3s16(1,0,0), // right
1605 v3s16(0,0,-1), // front
1606 v3s16(-1,0,0), // left
1608 for(u16 i=0; i<5; i++)
1613 bool to_bottom = (i == 0);
1615 // If liquid is at lowest possible height, it's not going
1616 // anywhere except down
1617 if(liquid_level == 0 && to_bottom == false)
1620 u8 liquid_next_level = 0;
1621 // If going to bottom
1624 //liquid_next_level = 7;
1625 if(liquid_level >= 7 - WATER_DROP_BOOST)
1626 liquid_next_level = 7;
1628 liquid_next_level = liquid_level + WATER_DROP_BOOST;
1631 liquid_next_level = liquid_level - 1;
1633 bool n2_changed = false;
1634 bool flowed = false;
1636 v3s16 p2 = p0 + dirs_to[i];
1638 MapNode n2 = getNode(p2);
1639 //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
1641 if(content_liquid(n2.d))
1643 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1644 // Check that the liquids are the same type
1645 if(n2_nonsource_c != nonsource_c)
1647 dstream<<"WARNING: Not handling: different liquids"
1648 " collide"<<std::endl;
1651 bool n2_is_source = !content_flowing_liquid(n2.d);
1652 u8 n2_liquid_level = 8;
1653 if(n2_is_source == false)
1654 n2_liquid_level = n2.param2 & 0x07;
1663 // Just flow into the source, nothing changes.
1664 // n2_changed is not set because destination didn't change
1669 if(liquid_next_level > liquid_level)
1671 n2.param2 = liquid_next_level;
1679 else if(n2.d == CONTENT_AIR)
1682 n2.param2 = liquid_next_level;
1689 //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
1693 m_transforming_liquid.push_back(p2);
1695 v3s16 blockpos = getNodeBlockPos(p2);
1696 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1698 modified_blocks.insert(blockpos, block);
1701 // If n2_changed to bottom, don't flow anywhere else
1702 if(to_bottom && flowed && !is_source)
1705 }catch(InvalidPositionException &e)
1711 //if(loopcount >= 100000)
1712 if(loopcount >= initial_size * 1)
1715 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1718 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1720 v3s16 blockpos = getNodeBlockPos(p);
1721 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1722 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1725 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1729 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1733 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1735 v3s16 blockpos = getNodeBlockPos(p);
1736 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1737 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1740 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1744 block->m_node_metadata.set(p_rel, meta);
1747 void Map::removeNodeMetadata(v3s16 p)
1749 v3s16 blockpos = getNodeBlockPos(p);
1750 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1751 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1754 dstream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1758 block->m_node_metadata.remove(p_rel);
1761 void Map::nodeMetadataStep(float dtime,
1762 core::map<v3s16, MapBlock*> &changed_blocks)
1766 Currently there is no way to ensure that all the necessary
1767 blocks are loaded when this is run. (They might get unloaded)
1768 NOTE: ^- Actually, that might not be so. In a quick test it
1769 reloaded a block with a furnace when I walked back to it from
1772 core::map<v2s16, MapSector*>::Iterator si;
1773 si = m_sectors.getIterator();
1774 for(; si.atEnd() == false; si++)
1776 MapSector *sector = si.getNode()->getValue();
1777 core::list< MapBlock * > sectorblocks;
1778 sector->getBlocks(sectorblocks);
1779 core::list< MapBlock * >::Iterator i;
1780 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1782 MapBlock *block = *i;
1783 bool changed = block->m_node_metadata.step(dtime);
1785 changed_blocks[block->getPos()] = block;
1794 ServerMap::ServerMap(std::string savedir):
1797 m_map_metadata_changed(true)
1799 dstream<<__FUNCTION_NAME<<std::endl;
1802 //m_chunksize = 16; // Too slow
1803 m_chunksize = 8; // Takes a few seconds
1807 m_seed = (((u64)(myrand()%0xffff)<<0)
1808 + ((u64)(myrand()%0xffff)<<16)
1809 + ((u64)(myrand()%0xffff)<<32)
1810 + ((u64)(myrand()%0xffff)<<48));
1813 Experimental and debug stuff
1820 Try to load map; if not found, create a new one.
1823 m_savedir = savedir;
1824 m_map_saving_enabled = false;
1828 // If directory exists, check contents and load if possible
1829 if(fs::PathExists(m_savedir))
1831 // If directory is empty, it is safe to save into it.
1832 if(fs::GetDirListing(m_savedir).size() == 0)
1834 dstream<<DTIME<<"Server: Empty save directory is valid."
1836 m_map_saving_enabled = true;
1841 // Load map metadata (seed, chunksize)
1844 // Load chunk metadata
1847 catch(FileNotGoodException &e){
1848 dstream<<DTIME<<"WARNING: Server: Could not load "
1849 <<"metafile(s). Disabling chunk-based "
1850 <<"generation."<<std::endl;
1854 /*// Load sector (0,0) and throw and exception on fail
1855 if(loadSectorFull(v2s16(0,0)) == false)
1856 throw LoadError("Failed to load sector (0,0)");*/
1858 /*dstream<<DTIME<<"Server: Successfully loaded chunk "
1859 "metadata and sector (0,0) from "<<savedir<<
1860 ", assuming valid save directory."
1863 dstream<<DTIME<<"INFO: Server: Successfully loaded map "
1864 <<"and chunk metadata from "<<savedir
1865 <<", assuming valid save directory."
1868 m_map_saving_enabled = true;
1869 // Map loaded, not creating new one
1873 // If directory doesn't exist, it is safe to save to it
1875 m_map_saving_enabled = true;
1878 catch(std::exception &e)
1880 dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
1881 <<", exception: "<<e.what()<<std::endl;
1882 dstream<<"Please remove the map or fix it."<<std::endl;
1883 dstream<<"WARNING: Map saving will be disabled."<<std::endl;
1886 dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
1888 // Create zero sector
1889 emergeSector(v2s16(0,0));
1891 // Initially write whole map
1895 ServerMap::~ServerMap()
1897 dstream<<__FUNCTION_NAME<<std::endl;
1901 if(m_map_saving_enabled)
1904 // Save only changed parts
1906 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1910 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1913 catch(std::exception &e)
1915 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1916 <<", exception: "<<e.what()<<std::endl;
1922 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1923 for(; i.atEnd() == false; i++)
1925 MapChunk *chunk = i.getNode()->getValue();
1931 Some helper functions for the map generator
1934 s16 find_ground_level(VoxelManipulator &vmanip, v2s16 p2d)
1936 v3s16 em = vmanip.m_area.getExtent();
1937 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
1938 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
1939 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
1941 for(y=y_nodes_max; y>=y_nodes_min; y--)
1943 MapNode &n = vmanip.m_data[i];
1944 if(content_walkable(n.d))
1947 vmanip.m_area.add_y(em, i, -1);
1949 if(y >= y_nodes_min)
1955 s16 find_ground_level_clever(VoxelManipulator &vmanip, v2s16 p2d)
1957 v3s16 em = vmanip.m_area.getExtent();
1958 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
1959 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
1960 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
1962 for(y=y_nodes_max; y>=y_nodes_min; y--)
1964 MapNode &n = vmanip.m_data[i];
1965 if(content_walkable(n.d)
1966 && n.d != CONTENT_TREE
1967 && n.d != CONTENT_LEAVES)
1970 vmanip.m_area.add_y(em, i, -1);
1972 if(y >= y_nodes_min)
1978 void make_tree(VoxelManipulator &vmanip, v3s16 p0)
1980 MapNode treenode(CONTENT_TREE);
1981 MapNode leavesnode(CONTENT_LEAVES);
1983 s16 trunk_h = myrand_range(3, 6);
1985 for(s16 ii=0; ii<trunk_h; ii++)
1987 if(vmanip.m_area.contains(p1))
1988 vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
1992 // p1 is now the last piece of the trunk
1995 VoxelArea leaves_a(v3s16(-2,-2,-2), v3s16(2,2,2));
1996 //SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
1997 Buffer<u8> leaves_d(leaves_a.getVolume());
1998 for(s32 i=0; i<leaves_a.getVolume(); i++)
2001 // Force leaves at near the end of the trunk
2004 for(s16 z=-d; z<=d; z++)
2005 for(s16 y=-d; y<=d; y++)
2006 for(s16 x=-d; x<=d; x++)
2008 leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
2012 // Add leaves randomly
2013 for(u32 iii=0; iii<7; iii++)
2018 myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
2019 myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
2020 myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
2023 for(s16 z=0; z<=d; z++)
2024 for(s16 y=0; y<=d; y++)
2025 for(s16 x=0; x<=d; x++)
2027 leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
2031 // Blit leaves to vmanip
2032 for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
2033 for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
2034 for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
2038 if(vmanip.m_area.contains(p) == false)
2040 u32 vi = vmanip.m_area.index(p);
2041 if(vmanip.m_data[vi].d != CONTENT_AIR)
2043 u32 i = leaves_a.index(x,y,z);
2044 if(leaves_d[i] == 1)
2045 vmanip.m_data[vi] = leavesnode;
2050 Noise functions. Make sure seed is mangled differently in each one.
2053 // Amount of trees per area in nodes
2054 double tree_amount_2d(u64 seed, v2s16 p)
2056 double noise = noise2d_perlin(
2057 0.5+(float)p.X/250, 0.5+(float)p.Y/250,
2059 double zeroval = -0.3;
2063 return 0.04 * (noise-zeroval) / (1.0-zeroval);
2066 #define AVERAGE_MUD_AMOUNT 4
2068 double base_rock_level_2d(u64 seed, v2s16 p)
2070 // The base ground level
2071 double base = (double)WATER_LEVEL - (double)AVERAGE_MUD_AMOUNT
2072 + 20. * noise2d_perlin(
2073 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2074 (seed>>32)+654879876, 6, 0.6);
2076 /*// A bit hillier one
2077 double base2 = WATER_LEVEL - 4.0 + 40. * noise2d_perlin(
2078 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2079 (seed>>27)+90340, 6, 0.69);
2083 // Higher ground level
2084 double higher = (double)WATER_LEVEL + 25. + 35. * noise2d_perlin(
2085 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2086 seed+85039, 5, 0.69);
2087 //higher = 30; // For debugging
2089 // Limit higher to at least base
2093 // Steepness factor of cliffs
2094 double b = 1.0 + 1.0 * noise2d_perlin(
2095 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2097 b = rangelim(b, 0.0, 1000.0);
2100 b = rangelim(b, 3.0, 1000.0);
2101 //dstream<<"b="<<b<<std::endl;
2104 // Offset to more low
2105 double a_off = -0.2;
2106 // High/low selector
2107 /*double a = 0.5 + b * (a_off + noise2d_perlin(
2108 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2109 seed-359, 6, 0.7));*/
2110 double a = (double)0.5 + b * (a_off + noise2d_perlin(
2111 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2112 seed-359, 5, 0.60));
2114 a = rangelim(a, 0.0, 1.0);
2116 //dstream<<"a="<<a<<std::endl;
2118 double h = base*(1.0-a) + higher*a;
2125 double get_mud_add_amount(u64 seed, v2s16 p)
2127 return ((float)AVERAGE_MUD_AMOUNT + 3.0 * noise2d_perlin(
2128 0.5+(float)p.X/200, 0.5+(float)p.Y/200,
2129 seed+91013, 3, 0.55));
2132 bool get_have_sand(u64 seed, v2s16 p2d)
2134 // Determine whether to have sand here
2135 double sandnoise = noise2d_perlin(
2136 0.5+(float)p2d.X/500, 0.5+(float)p2d.Y/500,
2137 seed+59420, 3, 0.50);
2139 return (sandnoise > -0.15);
2143 Adds random objects to block, depending on the content of the block
2145 void addRandomObjects(MapBlock *block)
2147 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2148 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2150 bool last_node_walkable = false;
2151 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2154 MapNode n = block->getNodeNoEx(p);
2155 if(n.d == CONTENT_IGNORE)
2157 if(content_features(n.d).liquid_type != LIQUID_NONE)
2159 if(content_features(n.d).walkable)
2161 last_node_walkable = true;
2164 if(last_node_walkable)
2166 // If block contains light information
2167 if(content_features(n.d).param_type == CPT_LIGHT)
2169 if(n.getLight(LIGHTBANK_DAY) <= 3)
2171 if(myrand() % 300 == 0)
2173 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
2175 ServerActiveObject *obj = new RatSAO(NULL, 0, pos_f);
2176 std::string data = obj->getStaticData();
2177 StaticObject s_obj(obj->getType(),
2178 obj->getBasePosition(), data);
2180 block->m_static_objects.insert(0, s_obj);
2181 block->m_static_objects.insert(0, s_obj);
2182 block->m_static_objects.insert(0, s_obj);
2183 block->m_static_objects.insert(0, s_obj);
2184 block->m_static_objects.insert(0, s_obj);
2185 block->m_static_objects.insert(0, s_obj);
2188 if(myrand() % 300 == 0)
2190 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
2192 ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f);
2193 std::string data = obj->getStaticData();
2194 StaticObject s_obj(obj->getType(),
2195 obj->getBasePosition(), data);
2197 block->m_static_objects.insert(0, s_obj);
2203 last_node_walkable = false;
2206 block->setChangedFlag();
2209 #define VMANIP_FLAG_DUNGEON VOXELFLAG_CHECKED1
2212 This is the main map generation method
2215 void makeChunk(ChunkMakeData *data)
2220 s16 y_nodes_min = data->y_blocks_min * MAP_BLOCKSIZE;
2221 s16 y_nodes_max = data->y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
2222 s16 h_blocks = data->y_blocks_max - data->y_blocks_min + 1;
2223 u32 relative_volume = (u32)data->sectorpos_base_size*MAP_BLOCKSIZE
2224 *(u32)data->sectorpos_base_size*MAP_BLOCKSIZE
2225 *(u32)h_blocks*MAP_BLOCKSIZE;
2226 v3s16 bigarea_blocks_min(
2227 data->sectorpos_bigbase.X,
2229 data->sectorpos_bigbase.Y
2231 v3s16 bigarea_blocks_max(
2232 data->sectorpos_bigbase.X + data->sectorpos_bigbase_size - 1,
2234 data->sectorpos_bigbase.Y + data->sectorpos_bigbase_size - 1
2236 s16 lighting_min_d = 0-data->max_spread_amount;
2237 s16 lighting_max_d = data->sectorpos_base_size*MAP_BLOCKSIZE
2238 + data->max_spread_amount-1;
2241 data->vmanip.clearFlag(0xff);
2243 TimeTaker timer_generate("makeChunk() generate");
2245 // Maximum height of the stone surface and obstacles.
2246 // This is used to disable cave generation from going too high.
2247 s16 stone_surface_max_y = 0;
2250 Generate general ground level to full area
2254 TimeTaker timer1("Generating ground level");
2257 NoiseBuffer noisebuf1;
2258 //NoiseBuffer noisebuf2;
2261 data->sectorpos_bigbase.X*MAP_BLOCKSIZE,
2263 data->sectorpos_bigbase.Y*MAP_BLOCKSIZE
2265 v3f maxpos_f = minpos_f + v3f(
2266 data->sectorpos_bigbase_size*MAP_BLOCKSIZE,
2267 y_nodes_max-y_nodes_min,
2268 data->sectorpos_bigbase_size*MAP_BLOCKSIZE
2270 v3f samplelength_f = v3f(4.0, 4.0, 4.0);
2272 TimeTaker timer("noisebuf.create");
2274 noisebuf1.create(data->seed+25104, 6, 0.60, 200.0,
2275 minpos_f.X, minpos_f.Y, minpos_f.Z,
2276 maxpos_f.X, maxpos_f.Y, maxpos_f.Z,
2277 samplelength_f.X, samplelength_f.Y, samplelength_f.Z);
2278 /*noisebuf1.create(data->seed+25104, 3, 0.60, 25.0,
2279 minpos_f.X, minpos_f.Y, minpos_f.Z,
2280 maxpos_f.X, maxpos_f.Y, maxpos_f.Z,
2281 samplelength_f.X, samplelength_f.Y, samplelength_f.Z);
2282 noisebuf2.create(data->seed+25105, 4, 0.50, 200.0,
2283 minpos_f.X, minpos_f.Y, minpos_f.Z,
2284 maxpos_f.X, maxpos_f.Y, maxpos_f.Z,
2285 samplelength_f.X, samplelength_f.Y, samplelength_f.Z);*/
2288 for(s16 x=0; x<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2289 for(s16 z=0; z<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2292 v2s16 p2d = data->sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2294 // Ground height at this point
2295 float surface_y_f = 0.0;
2297 // Use perlin noise for ground height
2298 surface_y_f = base_rock_level_2d(data->seed, p2d);
2299 //surface_y_f = base_rock_level_2d(data->seed, p2d);
2301 // Convert to integer
2302 s16 surface_y = (s16)surface_y_f;
2305 if(surface_y > stone_surface_max_y)
2306 stone_surface_max_y = surface_y;
2309 Fill ground with stone
2312 // Use fast index incrementing
2313 v3s16 em = data->vmanip.m_area.getExtent();
2314 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2315 for(s16 y=y_nodes_min; y<=y_nodes_max; y++)
2317 // Skip if already generated.
2318 // This is done here because there might be a cave at
2319 // any point in ground, which could look like it
2320 // wasn't generated.
2321 if(data->vmanip.m_data[i].d != CONTENT_AIR)
2324 /*s16 noiseval = 50.0 * noise3d_perlin(
2325 0.5+(float)p2d.X/100.0,
2327 0.5+(float)p2d.Y/100.0,
2328 data->seed+123, 5, 0.5);*/
2329 double noiseval = 64.0 * noisebuf1.get(p2d.X, y, p2d.Y);
2330 /*double noiseval = 30.0 * noisebuf1.get(p2d.X, y, p2d.Y);
2331 noiseval *= MYMAX(0, -0.2 + noisebuf2.get(p2d.X, y, p2d.Y));*/
2333 //if(y < surface_y + noiseval)
2336 data->vmanip.m_data[i].d = CONTENT_STONE;
2338 data->vmanip.m_area.add_y(em, i, 1);
2345 for(s16 x=0; x<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2346 for(s16 z=0; z<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2349 v2s16 p2d = data->sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2352 Skip of already generated
2355 v3s16 p(p2d.X, y_nodes_min, p2d.Y);
2356 if(data->vmanip.m_data[data->vmanip.m_area.index(p)].d != CONTENT_AIR)
2360 // Ground height at this point
2361 float surface_y_f = 0.0;
2363 // Use perlin noise for ground height
2364 surface_y_f = base_rock_level_2d(data->seed, p2d);
2366 /*// Experimental stuff
2368 float a = highlands_level_2d(data->seed, p2d);
2373 // Convert to integer
2374 s16 surface_y = (s16)surface_y_f;
2377 if(surface_y > stone_surface_max_y)
2378 stone_surface_max_y = surface_y;
2381 Fill ground with stone
2384 // Use fast index incrementing
2385 v3s16 em = data->vmanip.m_area.getExtent();
2386 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2387 for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++)
2389 // Skip if already generated.
2390 // This is done here because there might be a cave at
2391 // any point in ground, which could look like it
2392 // wasn't generated.
2393 if(data->vmanip.m_data[i].d != CONTENT_AIR)
2396 data->vmanip.m_data[i].d = CONTENT_STONE;
2398 data->vmanip.m_area.add_y(em, i, 1);
2407 Randomize some parameters
2410 //s32 stone_obstacle_count = 0;
2411 /*s32 stone_obstacle_count =
2412 rangelim((1.0+noise2d(data->seed+897,
2413 data->sectorpos_base.X, data->sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2415 //s16 stone_obstacle_max_height = 0;
2416 /*s16 stone_obstacle_max_height =
2417 rangelim((1.0+noise2d(data->seed+5902,
2418 data->sectorpos_base.X, data->sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2421 Loop this part, it will make stuff look older and newer nicely
2423 const u32 age_loops = 2;
2424 for(u32 i_age=0; i_age<age_loops; i_age++)
2426 /******************************
2427 BEGINNING OF AGING LOOP
2428 ******************************/
2433 //TimeTaker timer1("caves");
2438 u32 caves_count = relative_volume / 400000;
2439 u32 bruises_count = relative_volume * stone_surface_max_y / 40000000;
2440 if(stone_surface_max_y < WATER_LEVEL)
2442 /*u32 caves_count = 0;
2443 u32 bruises_count = 0;*/
2444 for(u32 jj=0; jj<caves_count+bruises_count; jj++)
2446 s16 min_tunnel_diameter = 3;
2447 s16 max_tunnel_diameter = 5;
2448 u16 tunnel_routepoints = 20;
2450 v3f main_direction(0,0,0);
2452 bool bruise_surface = (jj > caves_count);
2456 min_tunnel_diameter = 5;
2457 max_tunnel_diameter = myrand_range(10, 20);
2458 /*min_tunnel_diameter = MYMAX(0, stone_surface_max_y/6);
2459 max_tunnel_diameter = myrand_range(MYMAX(0, stone_surface_max_y/6), MYMAX(0, stone_surface_max_y/2));*/
2461 /*s16 tunnel_rou = rangelim(25*(0.5+1.0*noise2d(data->seed+42,
2462 data->sectorpos_base.X, data->sectorpos_base.Y)), 0, 15);*/
2464 tunnel_routepoints = 5;
2470 // Allowed route area size in nodes
2472 data->sectorpos_base_size*MAP_BLOCKSIZE,
2473 h_blocks*MAP_BLOCKSIZE,
2474 data->sectorpos_base_size*MAP_BLOCKSIZE
2477 // Area starting point in nodes
2479 data->sectorpos_base.X*MAP_BLOCKSIZE,
2480 data->y_blocks_min*MAP_BLOCKSIZE,
2481 data->sectorpos_base.Y*MAP_BLOCKSIZE
2485 //(this should be more than the maximum radius of the tunnel)
2486 //s16 insure = 5; // Didn't work with max_d = 20
2488 s16 more = data->max_spread_amount - max_tunnel_diameter/2 - insure;
2489 ar += v3s16(1,0,1) * more * 2;
2490 of -= v3s16(1,0,1) * more;
2492 s16 route_y_min = 0;
2493 // Allow half a diameter + 7 over stone surface
2494 s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2 + 7;
2496 /*// If caves, don't go through surface too often
2497 if(bruise_surface == false)
2498 route_y_max -= myrand_range(0, max_tunnel_diameter*2);*/
2500 // Limit maximum to area
2501 route_y_max = rangelim(route_y_max, 0, ar.Y-1);
2505 /*// Minimum is at y=0
2506 route_y_min = -of.Y - 0;*/
2507 // Minimum is at y=max_tunnel_diameter/4
2508 //route_y_min = -of.Y + max_tunnel_diameter/4;
2509 //s16 min = -of.Y + max_tunnel_diameter/4;
2510 s16 min = -of.Y + 0;
2511 route_y_min = myrand_range(min, min + max_tunnel_diameter);
2512 route_y_min = rangelim(route_y_min, 0, route_y_max);
2515 /*dstream<<"route_y_min = "<<route_y_min
2516 <<", route_y_max = "<<route_y_max<<std::endl;*/
2518 s16 route_start_y_min = route_y_min;
2519 s16 route_start_y_max = route_y_max;
2521 // Start every 2nd cave from surface
2522 bool coming_from_surface = (jj % 2 == 0 && bruise_surface == false);
2524 if(coming_from_surface)
2526 route_start_y_min = -of.Y + stone_surface_max_y + 10;
2529 route_start_y_min = rangelim(route_start_y_min, 0, ar.Y-1);
2530 route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y-1);
2532 // Randomize starting position
2534 (float)(myrand()%ar.X)+0.5,
2535 (float)(myrand_range(route_start_y_min, route_start_y_max))+0.5,
2536 (float)(myrand()%ar.Z)+0.5
2539 MapNode airnode(CONTENT_AIR);
2542 Generate some tunnel starting from orp
2545 for(u16 j=0; j<tunnel_routepoints; j++)
2547 if(j%7==0 && bruise_surface == false)
2549 main_direction = v3f(
2550 ((float)(myrand()%20)-(float)10)/10,
2551 ((float)(myrand()%20)-(float)10)/30,
2552 ((float)(myrand()%20)-(float)10)/10
2554 main_direction *= (float)myrand_range(1, 3);
2558 s16 min_d = min_tunnel_diameter;
2559 s16 max_d = max_tunnel_diameter;
2560 s16 rs = myrand_range(min_d, max_d);
2565 maxlen = v3s16(rs*7,rs*7,rs*7);
2569 maxlen = v3s16(rs*4, myrand_range(1, rs*3), rs*4);
2574 if(coming_from_surface && j < 3)
2577 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2578 (float)(myrand()%(maxlen.Y*1))-(float)maxlen.Y,
2579 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2585 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2586 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2587 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2591 vec += main_direction;
2596 else if(rp.X >= ar.X)
2598 if(rp.Y < route_y_min)
2600 else if(rp.Y >= route_y_max)
2601 rp.Y = route_y_max-1;
2604 else if(rp.Z >= ar.Z)
2608 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2610 v3f fp = orp + vec * f;
2611 v3s16 cp(fp.X, fp.Y, fp.Z);
2614 s16 d1 = d0 + rs - 1;
2615 for(s16 z0=d0; z0<=d1; z0++)
2617 //s16 si = rs - MYMAX(0, abs(z0)-rs/4);
2618 s16 si = rs - MYMAX(0, abs(z0)-rs/7);
2619 for(s16 x0=-si; x0<=si-1; x0++)
2621 s16 maxabsxz = MYMAX(abs(x0), abs(z0));
2622 //s16 si2 = rs - MYMAX(0, maxabsxz-rs/4);
2623 s16 si2 = rs - MYMAX(0, maxabsxz-rs/7);
2624 //s16 si2 = rs - abs(x0);
2625 for(s16 y0=-si2+1+2; y0<=si2-1; y0++)
2631 /*if(isInArea(p, ar) == false)
2633 // Check only height
2634 if(y < 0 || y >= ar.Y)
2638 //assert(data->vmanip.m_area.contains(p));
2639 if(data->vmanip.m_area.contains(p) == false)
2641 dstream<<"WARNING: "<<__FUNCTION_NAME
2642 <<":"<<__LINE__<<": "
2643 <<"point not in area"
2648 // Just set it to air, it will be changed to
2650 u32 i = data->vmanip.m_area.index(p);
2651 data->vmanip.m_data[i] = airnode;
2653 if(bruise_surface == false)
2656 data->vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON;
2674 //TimeTaker timer1("ore veins");
2679 for(u32 jj=0; jj<relative_volume/1000; jj++)
2681 s16 max_vein_diameter = 3;
2683 // Allowed route area size in nodes
2685 data->sectorpos_base_size*MAP_BLOCKSIZE,
2686 h_blocks*MAP_BLOCKSIZE,
2687 data->sectorpos_base_size*MAP_BLOCKSIZE
2690 // Area starting point in nodes
2692 data->sectorpos_base.X*MAP_BLOCKSIZE,
2693 data->y_blocks_min*MAP_BLOCKSIZE,
2694 data->sectorpos_base.Y*MAP_BLOCKSIZE
2698 //(this should be more than the maximum radius of the tunnel)
2700 s16 more = data->max_spread_amount - max_vein_diameter/2 - insure;
2701 ar += v3s16(1,0,1) * more * 2;
2702 of -= v3s16(1,0,1) * more;
2704 // Randomize starting position
2706 (float)(myrand()%ar.X)+0.5,
2707 (float)(myrand()%ar.Y)+0.5,
2708 (float)(myrand()%ar.Z)+0.5
2711 // Randomize mineral
2713 if(myrand()%4 != 0 || (orp.Y + of.Y) > 10)
2714 mineral = MINERAL_COAL;
2716 mineral = MINERAL_IRON;
2719 Generate some vein starting from orp
2722 for(u16 j=0; j<2; j++)
2725 (float)(myrand()%ar.X)+0.5,
2726 (float)(myrand()%ar.Y)+0.5,
2727 (float)(myrand()%ar.Z)+0.5
2729 v3f vec = rp - orp;*/
2731 v3s16 maxlen(5, 5, 5);
2733 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2734 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2735 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2740 else if(rp.X >= ar.X)
2744 else if(rp.Y >= ar.Y)
2748 else if(rp.Z >= ar.Z)
2754 s16 max_d = max_vein_diameter;
2755 s16 rs = myrand_range(min_d, max_d);
2757 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2759 v3f fp = orp + vec * f;
2760 v3s16 cp(fp.X, fp.Y, fp.Z);
2762 s16 d1 = d0 + rs - 1;
2763 for(s16 z0=d0; z0<=d1; z0++)
2765 s16 si = rs - abs(z0);
2766 for(s16 x0=-si; x0<=si-1; x0++)
2768 s16 si2 = rs - abs(x0);
2769 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2771 // Don't put mineral to every place
2779 /*if(isInArea(p, ar) == false)
2781 // Check only height
2782 if(y < 0 || y >= ar.Y)
2786 assert(data->vmanip.m_area.contains(p));
2788 // Just set it to air, it will be changed to
2790 u32 i = data->vmanip.m_area.index(p);
2791 MapNode *n = &data->vmanip.m_data[i];
2792 if(n->d == CONTENT_STONE)
2810 TimeTaker timer1("add mud");
2813 Add mud to the central chunk
2816 for(s16 x=0; x<data->sectorpos_base_size*MAP_BLOCKSIZE; x++)
2817 for(s16 z=0; z<data->sectorpos_base_size*MAP_BLOCKSIZE; z++)
2819 // Node position in 2d
2820 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2822 // Randomize mud amount
2823 s16 mud_add_amount = get_mud_add_amount(data->seed, p2d) / 2.0;
2825 // Find ground level
2826 s16 surface_y = find_ground_level_clever(data->vmanip, p2d);
2829 If topmost node is grass, change it to mud.
2830 It might be if it was flown to there from a neighboring
2831 chunk and then converted.
2834 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y));
2835 MapNode *n = &data->vmanip.m_data[i];
2836 if(n->d == CONTENT_GRASS)
2837 *n = MapNode(CONTENT_MUD);
2838 //n->d = CONTENT_MUD;
2846 v3s16 em = data->vmanip.m_area.getExtent();
2847 s16 y_start = surface_y+1;
2848 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2849 for(s16 y=y_start; y<=y_nodes_max; y++)
2851 if(mudcount >= mud_add_amount)
2854 MapNode &n = data->vmanip.m_data[i];
2855 n = MapNode(CONTENT_MUD);
2856 //n.d = CONTENT_MUD;
2859 data->vmanip.m_area.add_y(em, i, 1);
2871 TimeTaker timer1("flow mud");
2874 Flow mud away from steep edges
2877 // Limit area by 1 because mud is flown into neighbors.
2878 s16 mudflow_minpos = 0-data->max_spread_amount+1;
2879 s16 mudflow_maxpos = data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-2;
2881 // Iterate a few times
2882 for(s16 k=0; k<3; k++)
2885 for(s16 x=mudflow_minpos;
2888 for(s16 z=mudflow_minpos;
2892 // Invert coordinates every 2nd iteration
2895 x = mudflow_maxpos - (x-mudflow_minpos);
2896 z = mudflow_maxpos - (z-mudflow_minpos);
2899 // Node position in 2d
2900 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2902 v3s16 em = data->vmanip.m_area.getExtent();
2903 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2910 for(; y>=y_nodes_min; y--)
2912 n = &data->vmanip.m_data[i];
2913 //if(content_walkable(n->d))
2915 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2918 data->vmanip.m_area.add_y(em, i, -1);
2921 // Stop if out of area
2922 //if(data->vmanip.m_area.contains(i) == false)
2926 /*// If not mud, do nothing to it
2927 MapNode *n = &data->vmanip.m_data[i];
2928 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
2932 Don't flow it if the stuff under it is not mud
2936 data->vmanip.m_area.add_y(em, i2, -1);
2937 // Cancel if out of area
2938 if(data->vmanip.m_area.contains(i2) == false)
2940 MapNode *n2 = &data->vmanip.m_data[i2];
2941 if(n2->d != CONTENT_MUD && n2->d != CONTENT_GRASS)
2945 // Make it exactly mud
2948 /*s16 recurse_count = 0;
2952 v3s16(0,0,1), // back
2953 v3s16(1,0,0), // right
2954 v3s16(0,0,-1), // front
2955 v3s16(-1,0,0), // left
2958 // Theck that upper is air or doesn't exist.
2959 // Cancel dropping if upper keeps it in place
2961 data->vmanip.m_area.add_y(em, i3, 1);
2962 if(data->vmanip.m_area.contains(i3) == true
2963 && content_walkable(data->vmanip.m_data[i3].d) == true)
2970 for(u32 di=0; di<4; di++)
2972 v3s16 dirp = dirs4[di];
2975 data->vmanip.m_area.add_p(em, i2, dirp);
2976 // Fail if out of area
2977 if(data->vmanip.m_area.contains(i2) == false)
2979 // Check that side is air
2980 MapNode *n2 = &data->vmanip.m_data[i2];
2981 if(content_walkable(n2->d))
2983 // Check that under side is air
2984 data->vmanip.m_area.add_y(em, i2, -1);
2985 if(data->vmanip.m_area.contains(i2) == false)
2987 n2 = &data->vmanip.m_data[i2];
2988 if(content_walkable(n2->d))
2990 /*// Check that under that is air (need a drop of 2)
2991 data->vmanip.m_area.add_y(em, i2, -1);
2992 if(data->vmanip.m_area.contains(i2) == false)
2994 n2 = &data->vmanip.m_data[i2];
2995 if(content_walkable(n2->d))
2997 // Loop further down until not air
2999 data->vmanip.m_area.add_y(em, i2, -1);
3000 // Fail if out of area
3001 if(data->vmanip.m_area.contains(i2) == false)
3003 n2 = &data->vmanip.m_data[i2];
3004 }while(content_walkable(n2->d) == false);
3005 // Loop one up so that we're in air
3006 data->vmanip.m_area.add_y(em, i2, 1);
3007 n2 = &data->vmanip.m_data[i2];
3009 // Move mud to new place
3011 // Set old place to be air
3012 *n = MapNode(CONTENT_AIR);
3028 TimeTaker timer1("add water");
3031 Add water to the central chunk (and a bit more)
3034 for(s16 x=0-data->max_spread_amount;
3035 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3037 for(s16 z=0-data->max_spread_amount;
3038 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3041 // Node position in 2d
3042 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3044 // Find ground level
3045 //s16 surface_y = find_ground_level(data->vmanip, p2d);
3048 If ground level is over water level, skip.
3049 NOTE: This leaves caves near water without water,
3050 which looks especially crappy when the nearby water
3051 won't start flowing either for some reason
3053 /*if(surface_y > WATER_LEVEL)
3060 v3s16 em = data->vmanip.m_area.getExtent();
3061 u8 light = LIGHT_MAX;
3062 // Start at global water surface level
3063 s16 y_start = WATER_LEVEL;
3064 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3065 MapNode *n = &data->vmanip.m_data[i];
3067 for(s16 y=y_start; y>=y_nodes_min; y--)
3069 n = &data->vmanip.m_data[i];
3071 // Stop when there is no water and no air
3072 if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
3073 && n->d != CONTENT_WATER)
3079 // Make water only not in caves
3080 if(!(data->vmanip.m_flags[i]&VMANIP_FLAG_DUNGEON))
3082 n->d = CONTENT_WATERSOURCE;
3083 //n->setLight(LIGHTBANK_DAY, light);
3085 // Add to transforming liquid queue (in case it'd
3087 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3088 data->transforming_liquid.push_back(p);
3092 data->vmanip.m_area.add_y(em, i, -1);
3104 /***********************
3106 ************************/
3110 //TimeTaker timer1("convert mud to sand");
3116 //s16 mud_add_amount = myrand_range(2, 4);
3117 //s16 mud_add_amount = 0;
3119 /*for(s16 x=0; x<data->sectorpos_base_size*MAP_BLOCKSIZE; x++)
3120 for(s16 z=0; z<data->sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3121 for(s16 x=0-data->max_spread_amount+1;
3122 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3124 for(s16 z=0-data->max_spread_amount+1;
3125 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3128 // Node position in 2d
3129 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3131 bool have_sand = get_have_sand(data->seed, p2d);
3133 if(have_sand == false)
3136 // Find ground level
3137 s16 surface_y = find_ground_level_clever(data->vmanip, p2d);
3139 if(surface_y > WATER_LEVEL + 2)
3143 v3s16 em = data->vmanip.m_area.getExtent();
3144 s16 y_start = surface_y;
3145 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3146 u32 not_sand_counter = 0;
3147 for(s16 y=y_start; y>=y_nodes_min; y--)
3149 MapNode *n = &data->vmanip.m_data[i];
3150 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
3152 n->d = CONTENT_SAND;
3157 if(not_sand_counter > 3)
3161 data->vmanip.m_area.add_y(em, i, -1);
3173 //TimeTaker timer1("generate trees");
3179 // Divide area into parts
3181 s16 sidelen = data->sectorpos_base_size*MAP_BLOCKSIZE / div;
3182 double area = sidelen * sidelen;
3183 for(s16 x0=0; x0<div; x0++)
3184 for(s16 z0=0; z0<div; z0++)
3186 // Center position of part of division
3188 data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen/2 + sidelen*x0,
3189 data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen/2 + sidelen*z0
3191 // Minimum edge of part of division
3193 data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen*x0,
3194 data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen*z0
3196 // Maximum edge of part of division
3198 data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen + sidelen*x0 - 1,
3199 data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen + sidelen*z0 - 1
3202 u32 tree_count = area * tree_amount_2d(data->seed, p2d_center);
3203 // Put trees in random places on part of division
3204 for(u32 i=0; i<tree_count; i++)
3206 s16 x = myrand_range(p2d_min.X, p2d_max.X);
3207 s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
3208 s16 y = find_ground_level(data->vmanip, v2s16(x,z));
3209 // Don't make a tree under water level
3212 // Don't make a tree so high that it doesn't fit
3213 if(y > y_nodes_max - 6)
3217 Trees grow only on mud and grass
3220 u32 i = data->vmanip.m_area.index(v3s16(p));
3221 MapNode *n = &data->vmanip.m_data[i];
3222 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
3227 make_tree(data->vmanip, p);
3230 /*u32 tree_max = relative_area / 60;
3231 //u32 count = myrand_range(0, tree_max);
3232 for(u32 i=0; i<count; i++)
3234 s16 x = myrand_range(0, data->sectorpos_base_size*MAP_BLOCKSIZE-1);
3235 s16 z = myrand_range(0, data->sectorpos_base_size*MAP_BLOCKSIZE-1);
3236 x += data->sectorpos_base.X*MAP_BLOCKSIZE;
3237 z += data->sectorpos_base.Y*MAP_BLOCKSIZE;
3238 s16 y = find_ground_level(data->vmanip, v2s16(x,z));
3239 // Don't make a tree under water level
3244 make_tree(data->vmanip, p);
3254 //TimeTaker timer1("grow grass");
3260 /*for(s16 x=0-4; x<data->sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
3261 for(s16 z=0-4; z<data->sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
3262 for(s16 x=0-data->max_spread_amount;
3263 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3265 for(s16 z=0-data->max_spread_amount;
3266 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3269 // Node position in 2d
3270 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3273 Find the lowest surface to which enough light ends up
3276 Basically just wait until not air and not leaves.
3280 v3s16 em = data->vmanip.m_area.getExtent();
3281 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
3283 // Go to ground level
3284 for(y=y_nodes_max; y>=y_nodes_min; y--)
3286 MapNode &n = data->vmanip.m_data[i];
3287 if(n.d != CONTENT_AIR
3288 && n.d != CONTENT_LEAVES)
3290 data->vmanip.m_area.add_y(em, i, -1);
3292 if(y >= y_nodes_min)
3295 surface_y = y_nodes_min;
3298 u32 i = data->vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
3299 MapNode *n = &data->vmanip.m_data[i];
3300 if(n->d == CONTENT_MUD)
3301 n->d = CONTENT_GRASS;
3308 Initial lighting (sunlight)
3311 core::map<v3s16, bool> light_sources;
3314 // 750ms @cs=8, can't optimize more
3315 TimeTaker timer1("initial lighting");
3317 // NOTE: This is no used... umm... for some reason!
3320 Go through the edges and add all nodes that have light to light_sources
3324 for(s16 i=0; i<4; i++)
3326 for(s16 j=lighting_min_d;
3333 if(i == 0 || i == 1)
3335 x = (i==0) ? lighting_min_d : lighting_max_d;
3344 z = (i==0) ? lighting_min_d : lighting_max_d;
3351 // Node position in 2d
3352 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3355 v3s16 em = data->vmanip.m_area.getExtent();
3356 s16 y_start = y_nodes_max;
3357 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3358 for(s16 y=y_start; y>=y_nodes_min; y--)
3360 MapNode *n = &data->vmanip.m_data[i];
3361 if(n->getLight(LIGHTBANK_DAY) != 0)
3363 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3365 //NOTE: This is broken, at least the index has to
3374 Go through the edges and apply sunlight to them, not caring
3379 for(s16 i=0; i<4; i++)
3381 for(s16 j=lighting_min_d;
3388 if(i == 0 || i == 1)
3390 x = (i==0) ? lighting_min_d : lighting_max_d;
3399 z = (i==0) ? lighting_min_d : lighting_max_d;
3406 // Node position in 2d
3407 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3409 // Loop from top to down
3411 u8 light = LIGHT_SUN;
3412 v3s16 em = data->vmanip.m_area.getExtent();
3413 s16 y_start = y_nodes_max;
3414 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3415 for(s16 y=y_start; y>=y_nodes_min; y--)
3417 MapNode *n = &data->vmanip.m_data[i];
3418 if(light_propagates_content(n->d) == false)
3422 else if(light != LIGHT_SUN
3423 || sunlight_propagates_content(n->d) == false)
3429 n->setLight(LIGHTBANK_DAY, light);
3430 n->setLight(LIGHTBANK_NIGHT, 0);
3434 // Insert light source
3435 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3438 // Increment index by y
3439 data->vmanip.m_area.add_y(em, i, -1);
3445 /*for(s16 x=0; x<data->sectorpos_base_size*MAP_BLOCKSIZE; x++)
3446 for(s16 z=0; z<data->sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3447 /*for(s16 x=0-data->max_spread_amount+1;
3448 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3450 for(s16 z=0-data->max_spread_amount+1;
3451 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3455 This has to be 1 smaller than the actual area, because
3456 neighboring nodes are checked.
3458 for(s16 x=lighting_min_d+1;
3459 x<=lighting_max_d-1;
3461 for(s16 z=lighting_min_d+1;
3462 z<=lighting_max_d-1;
3465 // Node position in 2d
3466 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3469 Apply initial sunlight
3472 u8 light = LIGHT_SUN;
3473 bool add_to_sources = false;
3474 v3s16 em = data->vmanip.m_area.getExtent();
3475 s16 y_start = y_nodes_max;
3476 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3477 for(s16 y=y_start; y>=y_nodes_min; y--)
3479 MapNode *n = &data->vmanip.m_data[i];
3481 if(light_propagates_content(n->d) == false)
3485 else if(light != LIGHT_SUN
3486 || sunlight_propagates_content(n->d) == false)
3492 // This doesn't take much time
3493 if(add_to_sources == false)
3496 Check sides. If side is not air or water, start
3497 adding to light_sources.
3500 v3s16(0,0,1), // back
3501 v3s16(1,0,0), // right
3502 v3s16(0,0,-1), // front
3503 v3s16(-1,0,0), // left
3505 for(u32 di=0; di<4; di++)
3507 v3s16 dirp = dirs4[di];
3509 data->vmanip.m_area.add_p(em, i2, dirp);
3510 MapNode *n2 = &data->vmanip.m_data[i2];
3512 n2->d != CONTENT_AIR
3513 && n2->d != CONTENT_WATERSOURCE
3514 && n2->d != CONTENT_WATER
3516 add_to_sources = true;
3522 n->setLight(LIGHTBANK_DAY, light);
3523 n->setLight(LIGHTBANK_NIGHT, 0);
3525 // This doesn't take much time
3526 if(light != 0 && add_to_sources)
3528 // Insert light source
3529 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3532 // Increment index by y
3533 data->vmanip.m_area.add_y(em, i, -1);
3541 // Spread light around
3543 TimeTaker timer("makeChunk() spreadLight");
3544 data->vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
3551 timer_generate.stop();
3554 //###################################################################
3555 //###################################################################
3556 //###################################################################
3557 //###################################################################
3558 //###################################################################
3559 //###################################################################
3560 //###################################################################
3561 //###################################################################
3562 //###################################################################
3563 //###################################################################
3564 //###################################################################
3565 //###################################################################
3566 //###################################################################
3567 //###################################################################
3568 //###################################################################
3570 void ServerMap::initChunkMake(ChunkMakeData &data, v2s16 chunkpos)
3572 if(m_chunksize == 0)
3580 // The distance how far into the neighbors the generator is allowed to go.
3581 s16 max_spread_amount_sectors = 2;
3582 assert(max_spread_amount_sectors <= m_chunksize);
3583 s16 max_spread_amount = max_spread_amount_sectors * MAP_BLOCKSIZE;
3585 s16 y_blocks_min = -4;
3586 s16 y_blocks_max = 3;
3588 v2s16 sectorpos_base = chunk_to_sector(chunkpos);
3589 s16 sectorpos_base_size = m_chunksize;
3591 v2s16 sectorpos_bigbase =
3592 sectorpos_base - v2s16(1,1) * max_spread_amount_sectors;
3593 s16 sectorpos_bigbase_size =
3594 sectorpos_base_size + 2 * max_spread_amount_sectors;
3597 const s16 limit = MAP_GENERATION_LIMIT / MAP_BLOCKSIZE;
3598 if(sectorpos_bigbase.X < -limit
3599 || sectorpos_bigbase.X + sectorpos_bigbase_size >= limit
3600 || sectorpos_bigbase.Y < -limit
3601 || sectorpos_bigbase.Y + sectorpos_bigbase_size >= limit)
3608 data.chunkpos = chunkpos;
3609 data.y_blocks_min = y_blocks_min;
3610 data.y_blocks_max = y_blocks_max;
3611 data.sectorpos_base = sectorpos_base;
3612 data.sectorpos_base_size = sectorpos_base_size;
3613 data.sectorpos_bigbase = sectorpos_bigbase;
3614 data.sectorpos_bigbase_size = sectorpos_bigbase_size;
3615 data.max_spread_amount = max_spread_amount;
3618 Create the whole area of this and the neighboring chunks
3621 TimeTaker timer("initChunkMake() create area");
3623 for(s16 x=0; x<sectorpos_bigbase_size; x++)
3624 for(s16 z=0; z<sectorpos_bigbase_size; z++)
3626 v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
3627 ServerMapSector *sector = createSector(sectorpos);
3630 for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
3632 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
3633 MapBlock *block = createBlock(blockpos);
3635 // Lighting won't be calculated
3636 //block->setLightingExpired(true);
3637 // Lighting will be calculated
3638 block->setLightingExpired(false);
3641 Block gets sunlight if this is true.
3643 This should be set to true when the top side of a block
3644 is completely exposed to the sky.
3646 Actually this doesn't matter now because the
3647 initial lighting is done here.
3649 block->setIsUnderground(y != y_blocks_max);
3655 Now we have a big empty area.
3657 Make a ManualMapVoxelManipulator that contains this and the
3661 v3s16 bigarea_blocks_min(
3662 sectorpos_bigbase.X,
3666 v3s16 bigarea_blocks_max(
3667 sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
3669 sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
3672 data.vmanip.setMap(this);
3675 TimeTaker timer("initChunkMake() initialEmerge");
3676 data.vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
3681 MapChunk* ServerMap::finishChunkMake(ChunkMakeData &data,
3682 core::map<v3s16, MapBlock*> &changed_blocks)
3688 Blit generated stuff to map
3692 //TimeTaker timer("generateChunkRaw() blitBackAll");
3693 data.vmanip.blitBackAll(&changed_blocks);
3697 Update day/night difference cache of the MapBlocks
3700 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
3701 i.atEnd() == false; i++)
3703 MapBlock *block = i.getNode()->getValue();
3704 block->updateDayNightDiff();
3709 Copy transforming liquid information
3711 while(data.transforming_liquid.size() > 0)
3713 v3s16 p = data.transforming_liquid.pop_front();
3714 m_transforming_liquid.push_back(p);
3718 Add random objects to blocks
3721 for(s16 x=0; x<data.sectorpos_base_size; x++)
3722 for(s16 z=0; z<data.sectorpos_base_size; z++)
3724 v2s16 sectorpos = data.sectorpos_base + v2s16(x,z);
3725 ServerMapSector *sector = createSector(sectorpos);
3728 for(s16 y=data.y_blocks_min; y<=data.y_blocks_max; y++)
3730 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
3731 MapBlock *block = createBlock(blockpos);
3732 addRandomObjects(block);
3738 Create chunk metadata
3741 for(s16 x=-1; x<=1; x++)
3742 for(s16 y=-1; y<=1; y++)
3744 v2s16 chunkpos0 = data.chunkpos + v2s16(x,y);
3745 // Add chunk meta information
3746 MapChunk *chunk = getChunk(chunkpos0);
3749 chunk = new MapChunk();
3750 m_chunks.insert(chunkpos0, chunk);
3752 //chunk->setIsVolatile(true);
3753 if(chunk->getGenLevel() > GENERATED_PARTLY)
3754 chunk->setGenLevel(GENERATED_PARTLY);
3758 Set central chunk non-volatile
3760 MapChunk *chunk = getChunk(data.chunkpos);
3763 //chunk->setIsVolatile(false);
3764 chunk->setGenLevel(GENERATED_FULLY);
3767 Save changed parts of map
3776 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
3777 core::map<v3s16, MapBlock*> &changed_blocks,
3780 DSTACK(__FUNCTION_NAME);
3783 Don't generate if already fully generated
3787 MapChunk *chunk = getChunk(chunkpos);
3788 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3790 dstream<<"generateChunkRaw(): Chunk "
3791 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
3792 <<" already generated"<<std::endl;
3797 dstream<<"generateChunkRaw(): Generating chunk "
3798 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
3801 TimeTaker timer("generateChunkRaw()");
3805 // Initialize generation
3806 initChunkMake(data, chunkpos);
3811 // Finalize generation
3812 MapChunk *chunk = finishChunkMake(data, changed_blocks);
3815 Return central chunk (which was requested)
3821 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
3822 core::map<v3s16, MapBlock*> &changed_blocks)
3824 dstream<<"generateChunk(): Generating chunk "
3825 <<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
3828 /*for(s16 x=-1; x<=1; x++)
3829 for(s16 y=-1; y<=1; y++)*/
3830 for(s16 x=-0; x<=0; x++)
3831 for(s16 y=-0; y<=0; y++)
3833 v2s16 chunkpos0 = chunkpos1 + v2s16(x,y);
3834 MapChunk *chunk = getChunk(chunkpos0);
3835 // Skip if already generated
3836 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3838 generateChunkRaw(chunkpos0, changed_blocks);
3841 assert(chunkNonVolatile(chunkpos1));
3843 MapChunk *chunk = getChunk(chunkpos1);
3848 ServerMapSector * ServerMap::createSector(v2s16 p2d)
3850 DSTACKF("%s: p2d=(%d,%d)",
3855 Check if it exists already in memory
3857 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3862 Try to load it from disk (with blocks)
3864 if(loadSectorFull(p2d) == true)
3866 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3869 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
3870 throw InvalidPositionException("");
3876 Do not create over-limit
3878 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3879 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3880 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3881 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3882 throw InvalidPositionException("createSector(): pos. over limit");
3885 Generate blank sector
3888 sector = new ServerMapSector(this, p2d);
3890 // Sector position on map in nodes
3891 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3896 m_sectors.insert(p2d, sector);
3902 MapSector * ServerMap::emergeSector(v2s16 p2d,
3903 core::map<v3s16, MapBlock*> &changed_blocks)
3905 DSTACK("%s: p2d=(%d,%d)",
3912 v2s16 chunkpos = sector_to_chunk(p2d);
3913 /*bool chunk_nonvolatile = false;
3914 MapChunk *chunk = getChunk(chunkpos);
3915 if(chunk && chunk->getIsVolatile() == false)
3916 chunk_nonvolatile = true;*/
3917 bool chunk_nonvolatile = chunkNonVolatile(chunkpos);
3920 If chunk is not fully generated, generate chunk
3922 if(chunk_nonvolatile == false)
3924 // Generate chunk and neighbors
3925 generateChunk(chunkpos, changed_blocks);
3929 Return sector if it exists now
3931 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3936 Try to load it from disk
3938 if(loadSectorFull(p2d) == true)
3940 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3943 dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
3944 throw InvalidPositionException("");
3950 generateChunk should have generated the sector
3954 dstream<<"WARNING: ServerMap::emergeSector: Cannot find sector ("
3955 <<p2d.X<<","<<p2d.Y<<" and chunk is already generated. "
3959 dstream<<"WARNING: Creating an empty sector."<<std::endl;
3961 return createSector(p2d);
3966 dstream<<"WARNING: Forcing regeneration of chunk."<<std::endl;
3969 generateChunkRaw(chunkpos, changed_blocks, true);
3972 Return sector if it exists now
3974 sector = getSectorNoGenerateNoEx(p2d);
3978 dstream<<"ERROR: Could not get sector from anywhere."<<std::endl;
3986 //return generateSector();
3991 NOTE: This is not used for main map generation, only for blocks
3992 that are very high or low
3994 MapBlock * ServerMap::generateBlock(
3996 MapBlock *original_dummy,
3997 ServerMapSector *sector,
3998 core::map<v3s16, MapBlock*> &changed_blocks,
3999 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4002 DSTACKF("%s: p=(%d,%d,%d)",
4006 // If chunks are disabled
4007 /*if(m_chunksize == 0)
4009 dstream<<"ServerMap::generateBlock(): Chunks disabled -> "
4010 <<"not generating."<<std::endl;
4014 /*dstream<<"generateBlock(): "
4015 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4018 MapBlock *block = original_dummy;
4020 v2s16 p2d(p.X, p.Z);
4022 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
4025 Do not generate over-limit
4027 if(blockpos_over_limit(p))
4029 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
4030 throw InvalidPositionException("generateBlock(): pos. over limit");
4034 If block doesn't exist, create one.
4035 If it exists, it is a dummy. In that case unDummify() it.
4037 NOTE: This already sets the map as the parent of the block
4041 block = sector->createBlankBlockNoInsert(block_y);
4045 // Remove the block so that nobody can get a half-generated one.
4046 sector->removeBlock(block);
4047 // Allocate the block to contain the generated data
4053 Generate a completely empty block
4055 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4056 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4058 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4062 block->setNode(v3s16(x0,y0,z0), n);
4067 Generate a proper block
4070 u8 water_material = CONTENT_WATERSOURCE;
4072 s32 lowest_ground_y = 32767;
4073 s32 highest_ground_y = -32768;
4075 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4076 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4078 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
4080 //s16 surface_y = 0;
4082 s16 mud_add_amount = get_mud_add_amount(m_seed, p2d_nodes+v2s16(x0,z0));
4084 s16 surface_y = base_rock_level_2d(m_seed, p2d_nodes+v2s16(x0,z0))
4086 // If chunks are disabled
4087 if(m_chunksize == 0)
4088 surface_y = WATER_LEVEL + 1;
4090 if(surface_y < lowest_ground_y)
4091 lowest_ground_y = surface_y;
4092 if(surface_y > highest_ground_y)
4093 highest_ground_y = surface_y;
4095 s32 surface_depth = AVERAGE_MUD_AMOUNT;
4097 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4099 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
4104 NOTE: If there are some man-made structures above the
4105 newly created block, they won't be taken into account.
4107 if(real_y > surface_y)
4108 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
4114 // If node is over heightmap y, it's air or water
4115 if(real_y > surface_y)
4117 // If under water level, it's water
4118 if(real_y <= WATER_LEVEL)
4120 n.d = water_material;
4121 n.setLight(LIGHTBANK_DAY,
4122 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
4124 Add to transforming liquid queue (in case it'd
4127 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
4128 m_transforming_liquid.push_back(real_pos);
4134 // Else it's ground or caves (air)
4137 // If it's surface_depth under ground, it's stone
4138 if(real_y <= surface_y - surface_depth)
4140 n.d = CONTENT_STONE;
4144 // It is mud if it is under the first ground
4145 // level or under water
4146 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
4152 n.d = CONTENT_GRASS;
4155 //n.d = CONTENT_MUD;
4157 /*// If under water level, it's mud
4158 if(real_y < WATER_LEVEL)
4160 // Only the topmost node is grass
4161 else if(real_y <= surface_y - 1)
4164 n.d = CONTENT_GRASS;*/
4168 block->setNode(v3s16(x0,y0,z0), n);
4173 Calculate some helper variables
4176 // Completely underground if the highest part of block is under lowest
4178 // This has to be very sure; it's probably one too strict now but
4179 // that's just better.
4180 bool completely_underground =
4181 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
4183 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
4185 bool mostly_underwater_surface = false;
4186 if(highest_ground_y < WATER_LEVEL
4187 && some_part_underground && !completely_underground)
4188 mostly_underwater_surface = true;
4191 Get local attributes
4194 //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
4196 float caves_amount = 0.5;
4201 NOTE: BEWARE: Too big amount of attribute points slows verything
4203 1 interpolation from 5000 points takes 2-3ms.
4205 //TimeTaker timer("generateBlock() local attribute retrieval");
4206 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
4207 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
4208 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
4212 //dstream<<"generateBlock(): Done"<<std::endl;
4218 // Initialize temporary table
4219 const s32 ued = MAP_BLOCKSIZE;
4220 bool underground_emptiness[ued*ued*ued];
4221 for(s32 i=0; i<ued*ued*ued; i++)
4223 underground_emptiness[i] = 0;
4230 Initialize orp and ors. Try to find if some neighboring
4231 MapBlock has a tunnel ended in its side
4235 (float)(myrand()%ued)+0.5,
4236 (float)(myrand()%ued)+0.5,
4237 (float)(myrand()%ued)+0.5
4240 bool found_existing = false;
4246 for(s16 y=0; y<ued; y++)
4247 for(s16 x=0; x<ued; x++)
4249 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4250 if(getNode(ap).d == CONTENT_AIR)
4252 orp = v3f(x+1,y+1,0);
4253 found_existing = true;
4254 goto continue_generating;
4258 catch(InvalidPositionException &e){}
4264 for(s16 y=0; y<ued; y++)
4265 for(s16 x=0; x<ued; x++)
4267 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4268 if(getNode(ap).d == CONTENT_AIR)
4270 orp = v3f(x+1,y+1,ued-1);
4271 found_existing = true;
4272 goto continue_generating;
4276 catch(InvalidPositionException &e){}
4282 for(s16 y=0; y<ued; y++)
4283 for(s16 z=0; z<ued; z++)
4285 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4286 if(getNode(ap).d == CONTENT_AIR)
4288 orp = v3f(0,y+1,z+1);
4289 found_existing = true;
4290 goto continue_generating;
4294 catch(InvalidPositionException &e){}
4300 for(s16 y=0; y<ued; y++)
4301 for(s16 z=0; z<ued; z++)
4303 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4304 if(getNode(ap).d == CONTENT_AIR)
4306 orp = v3f(ued-1,y+1,z+1);
4307 found_existing = true;
4308 goto continue_generating;
4312 catch(InvalidPositionException &e){}
4318 for(s16 x=0; x<ued; x++)
4319 for(s16 z=0; z<ued; z++)
4321 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4322 if(getNode(ap).d == CONTENT_AIR)
4324 orp = v3f(x+1,0,z+1);
4325 found_existing = true;
4326 goto continue_generating;
4330 catch(InvalidPositionException &e){}
4336 for(s16 x=0; x<ued; x++)
4337 for(s16 z=0; z<ued; z++)
4339 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4340 if(getNode(ap).d == CONTENT_AIR)
4342 orp = v3f(x+1,ued-1,z+1);
4343 found_existing = true;
4344 goto continue_generating;
4348 catch(InvalidPositionException &e){}
4350 continue_generating:
4353 Choose whether to actually generate cave
4355 bool do_generate_caves = true;
4356 // Don't generate if no part is underground
4357 if(!some_part_underground)
4359 do_generate_caves = false;
4361 // Don't generate if mostly underwater surface
4362 /*else if(mostly_underwater_surface)
4364 do_generate_caves = false;
4366 // Partly underground = cave
4367 else if(!completely_underground)
4369 do_generate_caves = (rand() % 100 <= (s32)(caves_amount*100));
4371 // Found existing cave underground
4372 else if(found_existing && completely_underground)
4374 do_generate_caves = (rand() % 100 <= (s32)(caves_amount*100));
4376 // Underground and no caves found
4379 do_generate_caves = (rand() % 300 <= (s32)(caves_amount*100));
4382 if(do_generate_caves)
4385 Generate some tunnel starting from orp and ors
4387 for(u16 i=0; i<3; i++)
4390 (float)(myrand()%ued)+0.5,
4391 (float)(myrand()%ued)+0.5,
4392 (float)(myrand()%ued)+0.5
4396 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
4400 for(float f=0; f<1.0; f+=0.04)
4402 v3f fp = orp + vec * f;
4403 v3s16 cp(fp.X, fp.Y, fp.Z);
4405 s16 d1 = d0 + rs - 1;
4406 for(s16 z0=d0; z0<=d1; z0++)
4408 s16 si = rs - abs(z0);
4409 for(s16 x0=-si; x0<=si-1; x0++)
4411 s16 si2 = rs - abs(x0);
4412 for(s16 y0=-si2+1; y0<=si2-1; y0++)
4418 if(isInArea(p, ued) == false)
4420 underground_emptiness[ued*ued*z + ued*y + x] = 1;
4432 // Set to true if has caves.
4433 // Set when some non-air is changed to air when making caves.
4434 bool has_caves = false;
4437 Apply temporary cave data to block
4440 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4441 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4443 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4445 MapNode n = block->getNode(v3s16(x0,y0,z0));
4448 if(underground_emptiness[
4449 ued*ued*(z0*ued/MAP_BLOCKSIZE)
4450 +ued*(y0*ued/MAP_BLOCKSIZE)
4451 +(x0*ued/MAP_BLOCKSIZE)])
4453 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
4462 block->setNode(v3s16(x0,y0,z0), n);
4467 This is used for guessing whether or not the block should
4468 receive sunlight from the top if the block above doesn't exist
4470 block->setIsUnderground(completely_underground);
4473 Force lighting update if some part of block is partly
4474 underground and has caves.
4476 /*if(some_part_underground && !completely_underground && has_caves)
4478 //dstream<<"Half-ground caves"<<std::endl;
4479 lighting_invalidated_blocks[block->getPos()] = block;
4482 // DEBUG: Always update lighting
4483 //lighting_invalidated_blocks[block->getPos()] = block;
4489 if(some_part_underground)
4491 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
4496 for(s16 i=0; i<underground_level/4 + 1; i++)
4498 if(myrand()%50 == 0)
4501 (myrand()%(MAP_BLOCKSIZE-2))+1,
4502 (myrand()%(MAP_BLOCKSIZE-2))+1,
4503 (myrand()%(MAP_BLOCKSIZE-2))+1
4509 for(u16 i=0; i<27; i++)
4511 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4513 block->setNode(cp+g_27dirs[i], n);
4521 u16 coal_amount = 30;
4522 u16 coal_rareness = 60 / coal_amount;
4523 if(coal_rareness == 0)
4525 if(myrand()%coal_rareness == 0)
4527 u16 a = myrand() % 16;
4528 u16 amount = coal_amount * a*a*a / 1000;
4529 for(s16 i=0; i<amount; i++)
4532 (myrand()%(MAP_BLOCKSIZE-2))+1,
4533 (myrand()%(MAP_BLOCKSIZE-2))+1,
4534 (myrand()%(MAP_BLOCKSIZE-2))+1
4538 n.d = CONTENT_STONE;
4539 n.param = MINERAL_COAL;
4541 for(u16 i=0; i<27; i++)
4543 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4545 block->setNode(cp+g_27dirs[i], n);
4553 u16 iron_amount = 8;
4554 u16 iron_rareness = 60 / iron_amount;
4555 if(iron_rareness == 0)
4557 if(myrand()%iron_rareness == 0)
4559 u16 a = myrand() % 16;
4560 u16 amount = iron_amount * a*a*a / 1000;
4561 for(s16 i=0; i<amount; i++)
4564 (myrand()%(MAP_BLOCKSIZE-2))+1,
4565 (myrand()%(MAP_BLOCKSIZE-2))+1,
4566 (myrand()%(MAP_BLOCKSIZE-2))+1
4570 n.d = CONTENT_STONE;
4571 n.param = MINERAL_IRON;
4573 for(u16 i=0; i<27; i++)
4575 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4577 block->setNode(cp+g_27dirs[i], n);
4584 Create a few rats in empty blocks underground
4586 if(completely_underground)
4588 //for(u16 i=0; i<2; i++)
4591 (myrand()%(MAP_BLOCKSIZE-2))+1,
4592 (myrand()%(MAP_BLOCKSIZE-2))+1,
4593 (myrand()%(MAP_BLOCKSIZE-2))+1
4596 // Check that the place is empty
4597 //if(!is_ground_content(block->getNode(cp).d))
4600 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp, BS));
4601 block->addObject(obj);
4606 #endif // end of proper block generation
4609 Add block to sector.
4611 sector->insertBlock(block);
4613 // Lighting is invalid after generation.
4614 block->setLightingExpired(true);
4621 <<"lighting_invalidated_blocks.size()"
4625 <<" "<<lighting_invalidated_blocks.size()
4627 <<", "<<completely_underground
4628 <<", "<<some_part_underground
4635 MapBlock * ServerMap::createBlock(v3s16 p)
4637 DSTACKF("%s: p=(%d,%d,%d)",
4638 __FUNCTION_NAME, p.X, p.Y, p.Z);
4641 Do not create over-limit
4643 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4644 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4645 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4646 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4647 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4648 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4649 throw InvalidPositionException("createBlock(): pos. over limit");
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;
4671 NOTE: This should not be done, or at least the exception
4672 should not be passed on as std::exception, because it
4673 won't be catched at all.
4675 /*catch(std::exception &e)
4677 dstream<<"createBlock: createSector() failed: "
4678 <<e.what()<<std::endl;
4683 Try to get a block from the sector
4686 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4690 block = sector->createBlankBlock(block_y);
4694 MapBlock * ServerMap::emergeBlock(
4696 bool only_from_disk,
4697 core::map<v3s16, MapBlock*> &changed_blocks,
4698 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4701 DSTACKF("%s: p=(%d,%d,%d), only_from_disk=%d",
4703 p.X, p.Y, p.Z, only_from_disk);
4706 Do not generate over-limit
4708 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4709 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4710 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4711 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4712 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4713 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4714 throw InvalidPositionException("emergeBlock(): pos. over limit");
4716 v2s16 p2d(p.X, p.Z);
4719 This will create or load a sector if not found in memory.
4720 If block exists on disk, it will be loaded.
4722 ServerMapSector *sector;
4724 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
4725 assert(sector->getId() == MAPSECTOR_SERVER);
4727 catch(InvalidPositionException &e)
4729 dstream<<"emergeBlock: emergeSector() failed: "
4730 <<e.what()<<std::endl;
4731 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4733 <<"You could try to delete it."<<std::endl;
4736 catch(VersionMismatchException &e)
4738 dstream<<"emergeBlock: emergeSector() failed: "
4739 <<e.what()<<std::endl;
4740 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4742 <<"You could try to delete it."<<std::endl;
4746 NOTE: This should not be done, or at least the exception
4747 should not be passed on as std::exception, because it
4748 won't be catched at all.
4750 /*catch(std::exception &e)
4752 dstream<<"emergeBlock: emergeSector() failed: "
4753 <<e.what()<<std::endl;
4754 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4756 <<"You could try to delete it."<<std::endl;
4761 Try to get a block from the sector
4764 bool does_not_exist = false;
4765 bool lighting_expired = false;
4766 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4770 does_not_exist = true;
4772 else if(block->isDummy() == true)
4774 does_not_exist = true;
4776 else if(block->getLightingExpired())
4778 lighting_expired = true;
4783 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
4788 If block was not found on disk and not going to generate a
4789 new one, make sure there is a dummy block in place.
4791 if(only_from_disk && (does_not_exist || lighting_expired))
4793 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
4797 // Create dummy block
4798 block = new MapBlock(this, p, true);
4800 // Add block to sector
4801 sector->insertBlock(block);
4807 //dstream<<"Not found on disk, generating."<<std::endl;
4809 //TimeTaker("emergeBlock() generate");
4811 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
4814 If the block doesn't exist, generate the block.
4818 block = generateBlock(p, block, sector, changed_blocks,
4819 lighting_invalidated_blocks);
4822 if(lighting_expired)
4824 lighting_invalidated_blocks.insert(p, block);
4828 Initially update sunlight
4832 core::map<v3s16, bool> light_sources;
4833 bool black_air_left = false;
4834 bool bottom_invalid =
4835 block->propagateSunlight(light_sources, true,
4838 // If sunlight didn't reach everywhere and part of block is
4839 // above ground, lighting has to be properly updated
4840 //if(black_air_left && some_part_underground)
4843 lighting_invalidated_blocks[block->getPos()] = block;
4848 lighting_invalidated_blocks[block->getPos()] = block;
4855 s16 ServerMap::findGroundLevel(v2s16 p2d)
4858 Uh, just do something random...
4860 // Find existing map from top to down
4863 v3s16 p(p2d.X, max, p2d.Y);
4864 for(; p.Y>min; p.Y--)
4866 MapNode n = getNodeNoEx(p);
4867 if(n.d != CONTENT_IGNORE)
4872 // If this node is not air, go to plan b
4873 if(getNodeNoEx(p).d != CONTENT_AIR)
4875 // Search existing walkable and return it
4876 for(; p.Y>min; p.Y--)
4878 MapNode n = getNodeNoEx(p);
4879 if(content_walkable(n.d) && n.d != CONTENT_IGNORE)
4885 Plan B: Get from map generator perlin noise function
4887 // This won't work if proper generation is disabled
4888 if(m_chunksize == 0)
4889 return WATER_LEVEL+2;
4890 double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
4894 void ServerMap::createDirs(std::string path)
4896 if(fs::CreateAllDirs(path) == false)
4898 m_dout<<DTIME<<"ServerMap: Failed to create directory "
4899 <<"\""<<path<<"\""<<std::endl;
4900 throw BaseException("ServerMap failed to create directory");
4904 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
4910 snprintf(cc, 9, "%.4x%.4x",
4911 (unsigned int)pos.X&0xffff,
4912 (unsigned int)pos.Y&0xffff);
4914 return m_savedir + "/sectors/" + cc;
4916 snprintf(cc, 9, "%.3x/%.3x",
4917 (unsigned int)pos.X&0xfff,
4918 (unsigned int)pos.Y&0xfff);
4920 return m_savedir + "/sectors2/" + cc;
4926 v2s16 ServerMap::getSectorPos(std::string dirname)
4930 size_t spos = dirname.rfind('/') + 1;
4931 assert(spos != std::string::npos);
4932 if(dirname.size() - spos == 8)
4935 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
4937 else if(dirname.size() - spos == 3)
4940 r = sscanf(dirname.substr(spos-4).c_str(), "%3x/%3x", &x, &y);
4941 // Sign-extend the 12 bit values up to 16 bits...
4942 if(x&0x800) x|=0xF000;
4943 if(y&0x800) y|=0xF000;
4950 v2s16 pos((s16)x, (s16)y);
4954 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
4956 v2s16 p2d = getSectorPos(sectordir);
4958 if(blockfile.size() != 4){
4959 throw InvalidFilenameException("Invalid block filename");
4962 int r = sscanf(blockfile.c_str(), "%4x", &y);
4964 throw InvalidFilenameException("Invalid block filename");
4965 return v3s16(p2d.X, y, p2d.Y);
4968 void ServerMap::save(bool only_changed)
4970 DSTACK(__FUNCTION_NAME);
4971 if(m_map_saving_enabled == false)
4973 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
4977 if(only_changed == false)
4978 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
4981 if(only_changed == false || m_map_metadata_changed)
4986 // Disable saving chunk metadata if chunks are disabled
4987 if(m_chunksize != 0)
4989 if(only_changed == false || anyChunkModified())
4993 u32 sector_meta_count = 0;
4994 u32 block_count = 0;
4995 u32 block_count_all = 0; // Number of blocks in memory
4997 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
4998 for(; i.atEnd() == false; i++)
5000 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
5001 assert(sector->getId() == MAPSECTOR_SERVER);
5003 if(sector->differs_from_disk || only_changed == false)
5005 saveSectorMeta(sector);
5006 sector_meta_count++;
5008 core::list<MapBlock*> blocks;
5009 sector->getBlocks(blocks);
5010 core::list<MapBlock*>::Iterator j;
5011 for(j=blocks.begin(); j!=blocks.end(); j++)
5013 MapBlock *block = *j;
5017 if(block->getChangedFlag() || only_changed == false)
5022 /*dstream<<"ServerMap: Written block ("
5023 <<block->getPos().X<<","
5024 <<block->getPos().Y<<","
5025 <<block->getPos().Z<<")"
5032 Only print if something happened or saved whole map
5034 if(only_changed == false || sector_meta_count != 0
5035 || block_count != 0)
5037 dstream<<DTIME<<"ServerMap: Written: "
5038 <<sector_meta_count<<" sector metadata files, "
5039 <<block_count<<" block files"
5040 <<", "<<block_count_all<<" blocks in memory."
5046 // NOTE: Doing this is insane. Deprecated and probably broken.
5047 void ServerMap::loadAll()
5049 DSTACK(__FUNCTION_NAME);
5050 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
5055 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
5057 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
5059 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5062 s32 printed_counter = -100000;
5063 s32 count = list.size();
5065 std::vector<fs::DirListNode>::iterator i;
5066 for(i=list.begin(); i!=list.end(); i++)
5068 if(counter > printed_counter + 10)
5070 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
5071 printed_counter = counter;
5075 MapSector *sector = NULL;
5077 // We want directories
5081 sector = loadSectorMeta(i->name);
5083 catch(InvalidFilenameException &e)
5085 // This catches unknown crap in directory
5088 std::vector<fs::DirListNode> list2 = fs::GetDirListing
5089 (m_savedir+"/sectors/"+i->name);
5090 std::vector<fs::DirListNode>::iterator i2;
5091 for(i2=list2.begin(); i2!=list2.end(); i2++)
5097 loadBlock(i->name, i2->name, sector);
5099 catch(InvalidFilenameException &e)
5101 // This catches unknown crap in directory
5105 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
5110 void ServerMap::saveMasterHeightmap()
5112 DSTACK(__FUNCTION_NAME);
5114 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
5116 createDir(m_savedir);
5118 /*std::string fullpath = m_savedir + "/master_heightmap";
5119 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5120 if(o.good() == false)
5121 throw FileNotGoodException("Cannot open master heightmap");*/
5123 // Format used for writing
5124 //u8 version = SER_FMT_VER_HIGHEST;
5127 void ServerMap::loadMasterHeightmap()
5129 DSTACK(__FUNCTION_NAME);
5131 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
5133 /*std::string fullpath = m_savedir + "/master_heightmap";
5134 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5135 if(is.good() == false)
5136 throw FileNotGoodException("Cannot open master heightmap");*/
5140 void ServerMap::saveMapMeta()
5142 DSTACK(__FUNCTION_NAME);
5144 dstream<<"INFO: ServerMap::saveMapMeta(): "
5145 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
5148 createDirs(m_savedir);
5150 std::string fullpath = m_savedir + "/map_meta.txt";
5151 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
5152 if(os.good() == false)
5154 dstream<<"ERROR: ServerMap::saveMapMeta(): "
5155 <<"could not open"<<fullpath<<std::endl;
5156 throw FileNotGoodException("Cannot open chunk metadata");
5160 params.setU64("seed", m_seed);
5161 params.setS32("chunksize", m_chunksize);
5163 params.writeLines(os);
5165 os<<"[end_of_params]\n";
5167 m_map_metadata_changed = false;
5170 void ServerMap::loadMapMeta()
5172 DSTACK(__FUNCTION_NAME);
5174 dstream<<"INFO: ServerMap::loadMapMeta(): Loading map metadata"
5177 std::string fullpath = m_savedir + "/map_meta.txt";
5178 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5179 if(is.good() == false)
5181 dstream<<"ERROR: ServerMap::loadMapMeta(): "
5182 <<"could not open"<<fullpath<<std::endl;
5183 throw FileNotGoodException("Cannot open map metadata");
5191 throw SerializationError
5192 ("ServerMap::loadMapMeta(): [end_of_params] not found");
5194 std::getline(is, line);
5195 std::string trimmedline = trim(line);
5196 if(trimmedline == "[end_of_params]")
5198 params.parseConfigLine(line);
5201 m_seed = params.getU64("seed");
5202 m_chunksize = params.getS32("chunksize");
5204 dstream<<"INFO: ServerMap::loadMapMeta(): "
5205 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
5209 void ServerMap::saveChunkMeta()
5211 DSTACK(__FUNCTION_NAME);
5213 // This should not be called if chunks are disabled.
5214 assert(m_chunksize != 0);
5216 u32 count = m_chunks.size();
5218 dstream<<"INFO: ServerMap::saveChunkMeta(): Saving metadata of "
5219 <<count<<" chunks"<<std::endl;
5221 createDirs(m_savedir);
5223 std::string fullpath = m_savedir + "/chunk_meta";
5224 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
5225 if(os.good() == false)
5227 dstream<<"ERROR: ServerMap::saveChunkMeta(): "
5228 <<"could not open"<<fullpath<<std::endl;
5229 throw FileNotGoodException("Cannot open chunk metadata");
5235 os.write((char*)&version, 1);
5240 writeU32(buf, count);
5241 os.write((char*)buf, 4);
5243 for(core::map<v2s16, MapChunk*>::Iterator
5244 i = m_chunks.getIterator();
5245 i.atEnd()==false; i++)
5247 v2s16 p = i.getNode()->getKey();
5248 MapChunk *chunk = i.getNode()->getValue();
5251 os.write((char*)buf, 4);
5253 chunk->serialize(os, version);
5256 setChunksNonModified();
5259 void ServerMap::loadChunkMeta()
5261 DSTACK(__FUNCTION_NAME);
5263 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading chunk metadata"
5266 std::string fullpath = m_savedir + "/chunk_meta";
5267 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5268 if(is.good() == false)
5270 dstream<<"ERROR: ServerMap::loadChunkMeta(): "
5271 <<"could not open"<<fullpath<<std::endl;
5272 throw FileNotGoodException("Cannot open chunk metadata");
5278 is.read((char*)&version, 1);
5283 is.read((char*)buf, 4);
5284 u32 count = readU32(buf);
5286 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading metadata of "
5287 <<count<<" chunks"<<std::endl;
5289 for(u32 i=0; i<count; i++)
5292 MapChunk *chunk = new MapChunk();
5294 is.read((char*)buf, 4);
5297 chunk->deSerialize(is, version);
5298 m_chunks.insert(p, chunk);
5302 void ServerMap::saveSectorMeta(ServerMapSector *sector)
5304 DSTACK(__FUNCTION_NAME);
5305 // Format used for writing
5306 u8 version = SER_FMT_VER_HIGHEST;
5308 v2s16 pos = sector->getPos();
5309 std::string dir = getSectorDir(pos);
5312 std::string fullpath = dir + "/meta";
5313 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5314 if(o.good() == false)
5315 throw FileNotGoodException("Cannot open sector metafile");
5317 sector->serialize(o, version);
5319 sector->differs_from_disk = false;
5322 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
5324 DSTACK(__FUNCTION_NAME);
5326 v2s16 p2d = getSectorPos(sectordir);
5328 ServerMapSector *sector = NULL;
5330 std::string fullpath = sectordir + "/meta";
5331 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5332 if(is.good() == false)
5334 // If the directory exists anyway, it probably is in some old
5335 // format. Just go ahead and create the sector.
5336 if(fs::PathExists(sectordir))
5338 dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
5339 <<fullpath<<" doesn't exist but directory does."
5340 <<" Continuing with a sector with no metadata."
5342 sector = new ServerMapSector(this, p2d);
5343 m_sectors.insert(p2d, sector);
5347 throw FileNotGoodException("Cannot open sector metafile");
5352 sector = ServerMapSector::deSerialize
5353 (is, this, p2d, m_sectors);
5355 saveSectorMeta(sector);
5358 sector->differs_from_disk = false;
5363 bool ServerMap::loadSectorFull(v2s16 p2d)
5365 DSTACK(__FUNCTION_NAME);
5367 MapSector *sector = NULL;
5369 // The directory layout we're going to load from.
5370 // 1 - original sectors/xxxxzzzz/
5371 // 2 - new sectors2/xxx/zzz/
5372 // If we load from anything but the latest structure, we will
5373 // immediately save to the new one, and remove the old.
5375 std::string sectordir1 = getSectorDir(p2d, 1);
5376 std::string sectordir;
5377 if(fs::PathExists(sectordir1))
5379 sectordir = sectordir1;
5384 sectordir = getSectorDir(p2d, 2);
5387 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5390 sector = loadSectorMeta(sectordir, loadlayout != 2);
5392 catch(InvalidFilenameException &e)
5396 catch(FileNotGoodException &e)
5400 catch(std::exception &e)
5408 std::vector<fs::DirListNode> list2 = fs::GetDirListing
5410 std::vector<fs::DirListNode>::iterator i2;
5411 for(i2=list2.begin(); i2!=list2.end(); i2++)
5417 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
5419 catch(InvalidFilenameException &e)
5421 // This catches unknown crap in directory
5427 dstream<<"Sector converted to new layout - deleting "<<
5428 sectordir1<<std::endl;
5429 fs::RecursiveDelete(sectordir1);
5436 void ServerMap::saveBlock(MapBlock *block)
5438 DSTACK(__FUNCTION_NAME);
5440 Dummy blocks are not written
5442 if(block->isDummy())
5444 /*v3s16 p = block->getPos();
5445 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
5446 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
5450 // Format used for writing
5451 u8 version = SER_FMT_VER_HIGHEST;
5453 v3s16 p3d = block->getPos();
5454 v2s16 p2d(p3d.X, p3d.Z);
5455 std::string dir = getSectorDir(p2d);
5459 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
5460 std::string fullpath = dir + "/" + cc;
5461 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5462 if(o.good() == false)
5463 throw FileNotGoodException("Cannot open block data");
5466 [0] u8 serialization version
5469 o.write((char*)&version, 1);
5472 block->serialize(o, version);
5474 // Write extra data stored on disk
5475 block->serializeDiskExtra(o, version);
5477 // We just wrote it to the disk so clear modified flag
5478 block->resetChangedFlag();
5481 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
5483 DSTACK(__FUNCTION_NAME);
5485 std::string fullpath = sectordir+"/"+blockfile;
5488 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5489 if(is.good() == false)
5490 throw FileNotGoodException("Cannot open block file");
5492 v3s16 p3d = getBlockPos(sectordir, blockfile);
5493 v2s16 p2d(p3d.X, p3d.Z);
5495 assert(sector->getPos() == p2d);
5497 u8 version = SER_FMT_VER_INVALID;
5498 is.read((char*)&version, 1);
5501 throw SerializationError("ServerMap::loadBlock(): Failed"
5502 " to read MapBlock version");
5504 /*u32 block_size = MapBlock::serializedLength(version);
5505 SharedBuffer<u8> data(block_size);
5506 is.read((char*)*data, block_size);*/
5508 // This will always return a sector because we're the server
5509 //MapSector *sector = emergeSector(p2d);
5511 MapBlock *block = NULL;
5512 bool created_new = false;
5514 block = sector->getBlockNoCreate(p3d.Y);
5516 catch(InvalidPositionException &e)
5518 block = sector->createBlankBlockNoInsert(p3d.Y);
5523 block->deSerialize(is, version);
5525 // Read extra data stored on disk
5526 block->deSerializeDiskExtra(is, version);
5528 // If it's a new block, insert it to the map
5530 sector->insertBlock(block);
5533 Save blocks loaded in old format in new format
5536 if(version < SER_FMT_VER_HIGHEST || save_after_load)
5541 // We just loaded it from the disk, so it's up-to-date.
5542 block->resetChangedFlag();
5545 catch(SerializationError &e)
5547 dstream<<"WARNING: Invalid block data on disk "
5548 "(SerializationError). "
5551 //" Ignoring. A new one will be generated.
5554 // TODO: Backup file; name is in fullpath.
5558 void ServerMap::PrintInfo(std::ostream &out)
5569 ClientMap::ClientMap(
5571 MapDrawControl &control,
5572 scene::ISceneNode* parent,
5573 scene::ISceneManager* mgr,
5577 scene::ISceneNode(parent, mgr, id),
5580 m_camera_position(0,0,0),
5581 m_camera_direction(0,0,1)
5583 m_camera_mutex.Init();
5584 assert(m_camera_mutex.IsInitialized());
5586 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
5587 BS*1000000,BS*1000000,BS*1000000);
5590 ClientMap::~ClientMap()
5592 /*JMutexAutoLock lock(mesh_mutex);
5601 MapSector * ClientMap::emergeSector(v2s16 p2d)
5603 DSTACK(__FUNCTION_NAME);
5604 // Check that it doesn't exist already
5606 return getSectorNoGenerate(p2d);
5608 catch(InvalidPositionException &e)
5613 ClientMapSector *sector = new ClientMapSector(this, p2d);
5616 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5617 m_sectors.insert(p2d, sector);
5623 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
5625 DSTACK(__FUNCTION_NAME);
5626 ClientMapSector *sector = NULL;
5628 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5630 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
5634 sector = (ClientMapSector*)n->getValue();
5635 assert(sector->getId() == MAPSECTOR_CLIENT);
5639 sector = new ClientMapSector(this, p2d);
5641 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5642 m_sectors.insert(p2d, sector);
5646 sector->deSerialize(is);
5649 void ClientMap::OnRegisterSceneNode()
5653 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5654 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5657 ISceneNode::OnRegisterSceneNode();
5660 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5662 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5663 DSTACK(__FUNCTION_NAME);
5665 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5668 This is called two times per frame, reset on the non-transparent one
5670 if(pass == scene::ESNRP_SOLID)
5672 m_last_drawn_sectors.clear();
5676 Get time for measuring timeout.
5678 Measuring time is very useful for long delays when the
5679 machine is swapping a lot.
5681 int time1 = time(0);
5683 //u32 daynight_ratio = m_client->getDayNightRatio();
5685 m_camera_mutex.Lock();
5686 v3f camera_position = m_camera_position;
5687 v3f camera_direction = m_camera_direction;
5688 m_camera_mutex.Unlock();
5691 Get all blocks and draw all visible ones
5694 v3s16 cam_pos_nodes(
5695 camera_position.X / BS,
5696 camera_position.Y / BS,
5697 camera_position.Z / BS);
5699 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5701 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5702 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5704 // Take a fair amount as we will be dropping more out later
5706 p_nodes_min.X / MAP_BLOCKSIZE - 1,
5707 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
5708 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
5710 p_nodes_max.X / MAP_BLOCKSIZE,
5711 p_nodes_max.Y / MAP_BLOCKSIZE,
5712 p_nodes_max.Z / MAP_BLOCKSIZE);
5714 u32 vertex_count = 0;
5716 // For limiting number of mesh updates per frame
5717 u32 mesh_update_count = 0;
5719 u32 blocks_would_have_drawn = 0;
5720 u32 blocks_drawn = 0;
5722 int timecheck_counter = 0;
5723 core::map<v2s16, MapSector*>::Iterator si;
5724 si = m_sectors.getIterator();
5725 for(; si.atEnd() == false; si++)
5728 timecheck_counter++;
5729 if(timecheck_counter > 50)
5731 timecheck_counter = 0;
5732 int time2 = time(0);
5733 if(time2 > time1 + 4)
5735 dstream<<"ClientMap::renderMap(): "
5736 "Rendering takes ages, returning."
5743 MapSector *sector = si.getNode()->getValue();
5744 v2s16 sp = sector->getPos();
5746 if(m_control.range_all == false)
5748 if(sp.X < p_blocks_min.X
5749 || sp.X > p_blocks_max.X
5750 || sp.Y < p_blocks_min.Z
5751 || sp.Y > p_blocks_max.Z)
5755 core::list< MapBlock * > sectorblocks;
5756 sector->getBlocks(sectorblocks);
5762 u32 sector_blocks_drawn = 0;
5764 core::list< MapBlock * >::Iterator i;
5765 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5767 MapBlock *block = *i;
5770 Compare block position to camera position, skip
5771 if not seen on display
5774 float range = 100000 * BS;
5775 if(m_control.range_all == false)
5776 range = m_control.wanted_range * BS;
5779 if(isBlockInSight(block->getPos(), camera_position,
5780 camera_direction, range, &d) == false)
5785 // This is ugly (spherical distance limit?)
5786 /*if(m_control.range_all == false &&
5787 d - 0.5*BS*MAP_BLOCKSIZE > range)
5792 Update expired mesh (used for day/night change)
5794 It doesn't work exactly like it should now with the
5795 tasked mesh update but whatever.
5798 bool mesh_expired = false;
5801 JMutexAutoLock lock(block->mesh_mutex);
5803 mesh_expired = block->getMeshExpired();
5805 // Mesh has not been expired and there is no mesh:
5806 // block has no content
5807 if(block->mesh == NULL && mesh_expired == false)
5811 f32 faraway = BS*50;
5812 //f32 faraway = m_control.wanted_range * BS;
5815 This has to be done with the mesh_mutex unlocked
5817 // Pretty random but this should work somewhat nicely
5818 if(mesh_expired && (
5819 (mesh_update_count < 3
5820 && (d < faraway || mesh_update_count < 2)
5823 (m_control.range_all && mesh_update_count < 20)
5826 /*if(mesh_expired && mesh_update_count < 6
5827 && (d < faraway || mesh_update_count < 3))*/
5829 mesh_update_count++;
5831 // Mesh has been expired: generate new mesh
5832 //block->updateMesh(daynight_ratio);
5833 m_client->addUpdateMeshTask(block->getPos());
5835 mesh_expired = false;
5840 Draw the faces of the block
5843 JMutexAutoLock lock(block->mesh_mutex);
5845 scene::SMesh *mesh = block->mesh;
5850 blocks_would_have_drawn++;
5851 if(blocks_drawn >= m_control.wanted_max_blocks
5852 && m_control.range_all == false
5853 && d > m_control.wanted_min_range * BS)
5857 sector_blocks_drawn++;
5859 u32 c = mesh->getMeshBufferCount();
5861 for(u32 i=0; i<c; i++)
5863 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
5864 const video::SMaterial& material = buf->getMaterial();
5865 video::IMaterialRenderer* rnd =
5866 driver->getMaterialRenderer(material.MaterialType);
5867 bool transparent = (rnd && rnd->isTransparent());
5868 // Render transparent on transparent pass and likewise.
5869 if(transparent == is_transparent_pass)
5872 This *shouldn't* hurt too much because Irrlicht
5873 doesn't change opengl textures if the old
5874 material is set again.
5876 driver->setMaterial(buf->getMaterial());
5877 driver->drawMeshBuffer(buf);
5878 vertex_count += buf->getVertexCount();
5882 } // foreach sectorblocks
5884 if(sector_blocks_drawn != 0)
5886 m_last_drawn_sectors[sp] = true;
5890 m_control.blocks_drawn = blocks_drawn;
5891 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
5893 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
5894 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
5897 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
5898 core::map<v3s16, MapBlock*> *affected_blocks)
5900 bool changed = false;
5902 Add it to all blocks touching it
5905 v3s16(0,0,0), // this
5906 v3s16(0,0,1), // back
5907 v3s16(0,1,0), // top
5908 v3s16(1,0,0), // right
5909 v3s16(0,0,-1), // front
5910 v3s16(0,-1,0), // bottom
5911 v3s16(-1,0,0), // left
5913 for(u16 i=0; i<7; i++)
5915 v3s16 p2 = p + dirs[i];
5916 // Block position of neighbor (or requested) node
5917 v3s16 blockpos = getNodeBlockPos(p2);
5918 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5919 if(blockref == NULL)
5921 // Relative position of requested node
5922 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5923 if(blockref->setTempMod(relpos, mod))
5928 if(changed && affected_blocks!=NULL)
5930 for(u16 i=0; i<7; i++)
5932 v3s16 p2 = p + dirs[i];
5933 // Block position of neighbor (or requested) node
5934 v3s16 blockpos = getNodeBlockPos(p2);
5935 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5936 if(blockref == NULL)
5938 affected_blocks->insert(blockpos, blockref);
5944 bool ClientMap::clearTempMod(v3s16 p,
5945 core::map<v3s16, MapBlock*> *affected_blocks)
5947 bool changed = false;
5949 v3s16(0,0,0), // this
5950 v3s16(0,0,1), // back
5951 v3s16(0,1,0), // top
5952 v3s16(1,0,0), // right
5953 v3s16(0,0,-1), // front
5954 v3s16(0,-1,0), // bottom
5955 v3s16(-1,0,0), // left
5957 for(u16 i=0; i<7; i++)
5959 v3s16 p2 = p + dirs[i];
5960 // Block position of neighbor (or requested) node
5961 v3s16 blockpos = getNodeBlockPos(p2);
5962 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5963 if(blockref == NULL)
5965 // Relative position of requested node
5966 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5967 if(blockref->clearTempMod(relpos))
5972 if(changed && affected_blocks!=NULL)
5974 for(u16 i=0; i<7; i++)
5976 v3s16 p2 = p + dirs[i];
5977 // Block position of neighbor (or requested) node
5978 v3s16 blockpos = getNodeBlockPos(p2);
5979 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5980 if(blockref == NULL)
5982 affected_blocks->insert(blockpos, blockref);
5988 void ClientMap::expireMeshes(bool only_daynight_diffed)
5990 TimeTaker timer("expireMeshes()");
5992 core::map<v2s16, MapSector*>::Iterator si;
5993 si = m_sectors.getIterator();
5994 for(; si.atEnd() == false; si++)
5996 MapSector *sector = si.getNode()->getValue();
5998 core::list< MapBlock * > sectorblocks;
5999 sector->getBlocks(sectorblocks);
6001 core::list< MapBlock * >::Iterator i;
6002 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
6004 MapBlock *block = *i;
6006 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
6012 JMutexAutoLock lock(block->mesh_mutex);
6013 if(block->mesh != NULL)
6015 /*block->mesh->drop();
6016 block->mesh = NULL;*/
6017 block->setMeshExpired(true);
6024 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
6026 assert(mapType() == MAPTYPE_CLIENT);
6029 v3s16 p = blockpos + v3s16(0,0,0);
6030 MapBlock *b = getBlockNoCreate(p);
6031 b->updateMesh(daynight_ratio);
6032 //b->setMeshExpired(true);
6034 catch(InvalidPositionException &e){}
6037 v3s16 p = blockpos + v3s16(-1,0,0);
6038 MapBlock *b = getBlockNoCreate(p);
6039 b->updateMesh(daynight_ratio);
6040 //b->setMeshExpired(true);
6042 catch(InvalidPositionException &e){}
6044 v3s16 p = blockpos + v3s16(0,-1,0);
6045 MapBlock *b = getBlockNoCreate(p);
6046 b->updateMesh(daynight_ratio);
6047 //b->setMeshExpired(true);
6049 catch(InvalidPositionException &e){}
6051 v3s16 p = blockpos + v3s16(0,0,-1);
6052 MapBlock *b = getBlockNoCreate(p);
6053 b->updateMesh(daynight_ratio);
6054 //b->setMeshExpired(true);
6056 catch(InvalidPositionException &e){}
6061 Update mesh of block in which the node is, and if the node is at the
6062 leading edge, update the appropriate leading blocks too.
6064 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
6072 v3s16 blockposes[4];
6073 for(u32 i=0; i<4; i++)
6075 v3s16 np = nodepos + dirs[i];
6076 blockposes[i] = getNodeBlockPos(np);
6077 // Don't update mesh of block if it has been done already
6078 bool already_updated = false;
6079 for(u32 j=0; j<i; j++)
6081 if(blockposes[j] == blockposes[i])
6083 already_updated = true;
6090 MapBlock *b = getBlockNoCreate(blockposes[i]);
6091 b->updateMesh(daynight_ratio);
6096 void ClientMap::PrintInfo(std::ostream &out)
6107 MapVoxelManipulator::MapVoxelManipulator(Map *map)
6112 MapVoxelManipulator::~MapVoxelManipulator()
6114 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
6118 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
6120 TimeTaker timer1("emerge", &emerge_time);
6122 // Units of these are MapBlocks
6123 v3s16 p_min = getNodeBlockPos(a.MinEdge);
6124 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
6126 VoxelArea block_area_nodes
6127 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6129 addArea(block_area_nodes);
6131 for(s32 z=p_min.Z; z<=p_max.Z; z++)
6132 for(s32 y=p_min.Y; y<=p_max.Y; y++)
6133 for(s32 x=p_min.X; x<=p_max.X; x++)
6136 core::map<v3s16, bool>::Node *n;
6137 n = m_loaded_blocks.find(p);
6141 bool block_data_inexistent = false;
6144 TimeTaker timer1("emerge load", &emerge_load_time);
6146 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
6147 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
6150 dstream<<std::endl;*/
6152 MapBlock *block = m_map->getBlockNoCreate(p);
6153 if(block->isDummy())
6154 block_data_inexistent = true;
6156 block->copyTo(*this);
6158 catch(InvalidPositionException &e)
6160 block_data_inexistent = true;
6163 if(block_data_inexistent)
6165 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6166 // Fill with VOXELFLAG_INEXISTENT
6167 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
6168 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
6170 s32 i = m_area.index(a.MinEdge.X,y,z);
6171 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
6175 m_loaded_blocks.insert(p, !block_data_inexistent);
6178 //dstream<<"emerge done"<<std::endl;
6182 SUGG: Add an option to only update eg. water and air nodes.
6183 This will make it interfere less with important stuff if
6186 void MapVoxelManipulator::blitBack
6187 (core::map<v3s16, MapBlock*> & modified_blocks)
6189 if(m_area.getExtent() == v3s16(0,0,0))
6192 //TimeTaker timer1("blitBack");
6194 /*dstream<<"blitBack(): m_loaded_blocks.size()="
6195 <<m_loaded_blocks.size()<<std::endl;*/
6198 Initialize block cache
6200 v3s16 blockpos_last;
6201 MapBlock *block = NULL;
6202 bool block_checked_in_modified = false;
6204 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
6205 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
6206 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
6210 u8 f = m_flags[m_area.index(p)];
6211 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
6214 MapNode &n = m_data[m_area.index(p)];
6216 v3s16 blockpos = getNodeBlockPos(p);
6221 if(block == NULL || blockpos != blockpos_last){
6222 block = m_map->getBlockNoCreate(blockpos);
6223 blockpos_last = blockpos;
6224 block_checked_in_modified = false;
6227 // Calculate relative position in block
6228 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
6230 // Don't continue if nothing has changed here
6231 if(block->getNode(relpos) == n)
6234 //m_map->setNode(m_area.MinEdge + p, n);
6235 block->setNode(relpos, n);
6238 Make sure block is in modified_blocks
6240 if(block_checked_in_modified == false)
6242 modified_blocks[blockpos] = block;
6243 block_checked_in_modified = true;
6246 catch(InvalidPositionException &e)
6252 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
6253 MapVoxelManipulator(map),
6254 m_create_area(false)
6258 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
6262 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
6264 // Just create the area so that it can be pointed to
6265 VoxelManipulator::emerge(a, caller_id);
6268 void ManualMapVoxelManipulator::initialEmerge(
6269 v3s16 blockpos_min, v3s16 blockpos_max)
6271 TimeTaker timer1("initialEmerge", &emerge_time);
6273 // Units of these are MapBlocks
6274 v3s16 p_min = blockpos_min;
6275 v3s16 p_max = blockpos_max;
6277 VoxelArea block_area_nodes
6278 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6280 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
6283 dstream<<"initialEmerge: area: ";
6284 block_area_nodes.print(dstream);
6285 dstream<<" ("<<size_MB<<"MB)";
6289 addArea(block_area_nodes);
6291 for(s32 z=p_min.Z; z<=p_max.Z; z++)
6292 for(s32 y=p_min.Y; y<=p_max.Y; y++)
6293 for(s32 x=p_min.X; x<=p_max.X; x++)
6296 core::map<v3s16, bool>::Node *n;
6297 n = m_loaded_blocks.find(p);
6301 bool block_data_inexistent = false;
6304 TimeTaker timer1("emerge load", &emerge_load_time);
6306 MapBlock *block = m_map->getBlockNoCreate(p);
6307 if(block->isDummy())
6308 block_data_inexistent = true;
6310 block->copyTo(*this);
6312 catch(InvalidPositionException &e)
6314 block_data_inexistent = true;
6317 if(block_data_inexistent)
6320 Mark area inexistent
6322 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6323 // Fill with VOXELFLAG_INEXISTENT
6324 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
6325 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
6327 s32 i = m_area.index(a.MinEdge.X,y,z);
6328 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
6332 m_loaded_blocks.insert(p, !block_data_inexistent);
6336 void ManualMapVoxelManipulator::blitBackAll(
6337 core::map<v3s16, MapBlock*> * modified_blocks)
6339 if(m_area.getExtent() == v3s16(0,0,0))
6343 Copy data of all blocks
6345 for(core::map<v3s16, bool>::Iterator
6346 i = m_loaded_blocks.getIterator();
6347 i.atEnd() == false; i++)
6349 bool existed = i.getNode()->getValue();
6350 if(existed == false)
6352 v3s16 p = i.getNode()->getKey();
6353 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
6356 dstream<<"WARNING: "<<__FUNCTION_NAME
6357 <<": got NULL block "
6358 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
6363 block->copyFrom(*this);
6366 modified_blocks->insert(p, block);