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));
2133 Adds random objects to block, depending on the content of the block
2135 void addRandomObjects(MapBlock *block)
2137 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2138 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2140 bool last_node_walkable = false;
2141 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2144 MapNode n = block->getNodeNoEx(p);
2145 if(n.d == CONTENT_IGNORE)
2147 if(content_features(n.d).liquid_type != LIQUID_NONE)
2149 if(content_features(n.d).walkable)
2151 last_node_walkable = true;
2154 if(last_node_walkable)
2156 // If block contains light information
2157 if(content_features(n.d).param_type == CPT_LIGHT)
2159 if(n.getLight(LIGHTBANK_DAY) <= 3)
2161 if(myrand() % 300 == 0)
2163 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
2165 ServerActiveObject *obj = new RatSAO(NULL, 0, pos_f);
2166 std::string data = obj->getStaticData();
2167 StaticObject s_obj(obj->getType(),
2168 obj->getBasePosition(), data);
2170 block->m_static_objects.insert(0, s_obj);
2171 block->m_static_objects.insert(0, s_obj);
2172 block->m_static_objects.insert(0, s_obj);
2173 block->m_static_objects.insert(0, s_obj);
2174 block->m_static_objects.insert(0, s_obj);
2175 block->m_static_objects.insert(0, s_obj);
2178 if(myrand() % 300 == 0)
2180 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
2182 ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f);
2183 std::string data = obj->getStaticData();
2184 StaticObject s_obj(obj->getType(),
2185 obj->getBasePosition(), data);
2187 block->m_static_objects.insert(0, s_obj);
2193 last_node_walkable = false;
2196 block->setChangedFlag();
2199 #define VMANIP_FLAG_DUNGEON VOXELFLAG_CHECKED1
2202 This is the main map generation method
2205 void makeChunk(ChunkMakeData *data)
2210 s16 y_nodes_min = data->y_blocks_min * MAP_BLOCKSIZE;
2211 s16 y_nodes_max = data->y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
2212 s16 h_blocks = data->y_blocks_max - data->y_blocks_min + 1;
2213 u32 relative_volume = (u32)data->sectorpos_base_size*MAP_BLOCKSIZE
2214 *(u32)data->sectorpos_base_size*MAP_BLOCKSIZE
2215 *(u32)h_blocks*MAP_BLOCKSIZE;
2216 v3s16 bigarea_blocks_min(
2217 data->sectorpos_bigbase.X,
2219 data->sectorpos_bigbase.Y
2221 v3s16 bigarea_blocks_max(
2222 data->sectorpos_bigbase.X + data->sectorpos_bigbase_size - 1,
2224 data->sectorpos_bigbase.Y + data->sectorpos_bigbase_size - 1
2226 s16 lighting_min_d = 0-data->max_spread_amount;
2227 s16 lighting_max_d = data->sectorpos_base_size*MAP_BLOCKSIZE
2228 + data->max_spread_amount-1;
2231 data->vmanip.clearFlag(0xff);
2233 TimeTaker timer_generate("makeChunk() generate");
2235 // Maximum height of the stone surface and obstacles.
2236 // This is used to disable cave generation from going too high.
2237 s16 stone_surface_max_y = 0;
2240 Generate general ground level to full area
2244 TimeTaker timer1("Generating ground level");
2247 NoiseBuffer noisebuf1;
2248 //NoiseBuffer noisebuf2;
2251 data->sectorpos_bigbase.X*MAP_BLOCKSIZE,
2253 data->sectorpos_bigbase.Y*MAP_BLOCKSIZE
2255 v3f maxpos_f = minpos_f + v3f(
2256 data->sectorpos_bigbase_size*MAP_BLOCKSIZE,
2257 y_nodes_max-y_nodes_min,
2258 data->sectorpos_bigbase_size*MAP_BLOCKSIZE
2260 v3f samplelength_f = v3f(4.0, 4.0, 4.0);
2262 TimeTaker timer("noisebuf.create");
2264 noisebuf1.create(data->seed+25104, 6, 0.60, 200.0,
2265 minpos_f.X, minpos_f.Y, minpos_f.Z,
2266 maxpos_f.X, maxpos_f.Y, maxpos_f.Z,
2267 samplelength_f.X, samplelength_f.Y, samplelength_f.Z);
2268 /*noisebuf1.create(data->seed+25104, 3, 0.60, 25.0,
2269 minpos_f.X, minpos_f.Y, minpos_f.Z,
2270 maxpos_f.X, maxpos_f.Y, maxpos_f.Z,
2271 samplelength_f.X, samplelength_f.Y, samplelength_f.Z);
2272 noisebuf2.create(data->seed+25105, 4, 0.50, 200.0,
2273 minpos_f.X, minpos_f.Y, minpos_f.Z,
2274 maxpos_f.X, maxpos_f.Y, maxpos_f.Z,
2275 samplelength_f.X, samplelength_f.Y, samplelength_f.Z);*/
2278 for(s16 x=0; x<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2279 for(s16 z=0; z<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2282 v2s16 p2d = data->sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2284 // Ground height at this point
2285 float surface_y_f = 0.0;
2287 // Use perlin noise for ground height
2288 surface_y_f = base_rock_level_2d(data->seed, p2d);
2289 //surface_y_f = base_rock_level_2d(data->seed, p2d);
2291 // Convert to integer
2292 s16 surface_y = (s16)surface_y_f;
2295 if(surface_y > stone_surface_max_y)
2296 stone_surface_max_y = surface_y;
2299 Fill ground with stone
2302 // Use fast index incrementing
2303 v3s16 em = data->vmanip.m_area.getExtent();
2304 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2305 for(s16 y=y_nodes_min; y<=y_nodes_max; y++)
2307 // Skip if already generated.
2308 // This is done here because there might be a cave at
2309 // any point in ground, which could look like it
2310 // wasn't generated.
2311 if(data->vmanip.m_data[i].d != CONTENT_AIR)
2314 /*s16 noiseval = 50.0 * noise3d_perlin(
2315 0.5+(float)p2d.X/100.0,
2317 0.5+(float)p2d.Y/100.0,
2318 data->seed+123, 5, 0.5);*/
2319 double noiseval = 64.0 * noisebuf1.get(p2d.X, y, p2d.Y);
2320 /*double noiseval = 30.0 * noisebuf1.get(p2d.X, y, p2d.Y);
2321 noiseval *= MYMAX(0, -0.2 + noisebuf2.get(p2d.X, y, p2d.Y));*/
2323 //if(y < surface_y + noiseval)
2326 data->vmanip.m_data[i].d = CONTENT_STONE;
2328 data->vmanip.m_area.add_y(em, i, 1);
2335 for(s16 x=0; x<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2336 for(s16 z=0; z<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2339 v2s16 p2d = data->sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2342 Skip of already generated
2345 v3s16 p(p2d.X, y_nodes_min, p2d.Y);
2346 if(data->vmanip.m_data[data->vmanip.m_area.index(p)].d != CONTENT_AIR)
2350 // Ground height at this point
2351 float surface_y_f = 0.0;
2353 // Use perlin noise for ground height
2354 surface_y_f = base_rock_level_2d(data->seed, p2d);
2356 /*// Experimental stuff
2358 float a = highlands_level_2d(data->seed, p2d);
2363 // Convert to integer
2364 s16 surface_y = (s16)surface_y_f;
2367 if(surface_y > stone_surface_max_y)
2368 stone_surface_max_y = surface_y;
2371 Fill ground with stone
2374 // Use fast index incrementing
2375 v3s16 em = data->vmanip.m_area.getExtent();
2376 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2377 for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++)
2379 // Skip if already generated.
2380 // This is done here because there might be a cave at
2381 // any point in ground, which could look like it
2382 // wasn't generated.
2383 if(data->vmanip.m_data[i].d != CONTENT_AIR)
2386 data->vmanip.m_data[i].d = CONTENT_STONE;
2388 data->vmanip.m_area.add_y(em, i, 1);
2397 Randomize some parameters
2400 //s32 stone_obstacle_count = 0;
2401 /*s32 stone_obstacle_count =
2402 rangelim((1.0+noise2d(data->seed+897,
2403 data->sectorpos_base.X, data->sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2405 //s16 stone_obstacle_max_height = 0;
2406 /*s16 stone_obstacle_max_height =
2407 rangelim((1.0+noise2d(data->seed+5902,
2408 data->sectorpos_base.X, data->sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2411 Loop this part, it will make stuff look older and newer nicely
2413 const u32 age_loops = 2;
2414 for(u32 i_age=0; i_age<age_loops; i_age++)
2416 /******************************
2417 BEGINNING OF AGING LOOP
2418 ******************************/
2423 //TimeTaker timer1("caves");
2428 u32 caves_count = relative_volume / 400000;
2429 u32 bruises_count = relative_volume * stone_surface_max_y / 40000000;
2430 if(stone_surface_max_y < WATER_LEVEL)
2432 /*u32 caves_count = 0;
2433 u32 bruises_count = 0;*/
2434 for(u32 jj=0; jj<caves_count+bruises_count; jj++)
2436 s16 min_tunnel_diameter = 3;
2437 s16 max_tunnel_diameter = 5;
2438 u16 tunnel_routepoints = 20;
2440 v3f main_direction(0,0,0);
2442 bool bruise_surface = (jj > caves_count);
2446 min_tunnel_diameter = 5;
2447 max_tunnel_diameter = myrand_range(10, 20);
2448 /*min_tunnel_diameter = MYMAX(0, stone_surface_max_y/6);
2449 max_tunnel_diameter = myrand_range(MYMAX(0, stone_surface_max_y/6), MYMAX(0, stone_surface_max_y/2));*/
2451 /*s16 tunnel_rou = rangelim(25*(0.5+1.0*noise2d(data->seed+42,
2452 data->sectorpos_base.X, data->sectorpos_base.Y)), 0, 15);*/
2454 tunnel_routepoints = 5;
2460 // Allowed route area size in nodes
2462 data->sectorpos_base_size*MAP_BLOCKSIZE,
2463 h_blocks*MAP_BLOCKSIZE,
2464 data->sectorpos_base_size*MAP_BLOCKSIZE
2467 // Area starting point in nodes
2469 data->sectorpos_base.X*MAP_BLOCKSIZE,
2470 data->y_blocks_min*MAP_BLOCKSIZE,
2471 data->sectorpos_base.Y*MAP_BLOCKSIZE
2475 //(this should be more than the maximum radius of the tunnel)
2476 //s16 insure = 5; // Didn't work with max_d = 20
2478 s16 more = data->max_spread_amount - max_tunnel_diameter/2 - insure;
2479 ar += v3s16(1,0,1) * more * 2;
2480 of -= v3s16(1,0,1) * more;
2482 s16 route_y_min = 0;
2483 // Allow half a diameter + 7 over stone surface
2484 s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2 + 7;
2486 /*// If caves, don't go through surface too often
2487 if(bruise_surface == false)
2488 route_y_max -= myrand_range(0, max_tunnel_diameter*2);*/
2490 // Limit maximum to area
2491 route_y_max = rangelim(route_y_max, 0, ar.Y-1);
2495 /*// Minimum is at y=0
2496 route_y_min = -of.Y - 0;*/
2497 // Minimum is at y=max_tunnel_diameter/4
2498 //route_y_min = -of.Y + max_tunnel_diameter/4;
2499 //s16 min = -of.Y + max_tunnel_diameter/4;
2500 s16 min = -of.Y + 0;
2501 route_y_min = myrand_range(min, min + max_tunnel_diameter);
2502 route_y_min = rangelim(route_y_min, 0, route_y_max);
2505 /*dstream<<"route_y_min = "<<route_y_min
2506 <<", route_y_max = "<<route_y_max<<std::endl;*/
2508 s16 route_start_y_min = route_y_min;
2509 s16 route_start_y_max = route_y_max;
2511 // Start every 2nd cave from surface
2512 bool coming_from_surface = (jj % 2 == 0 && bruise_surface == false);
2514 if(coming_from_surface)
2516 route_start_y_min = -of.Y + stone_surface_max_y + 10;
2519 route_start_y_min = rangelim(route_start_y_min, 0, ar.Y-1);
2520 route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y-1);
2522 // Randomize starting position
2524 (float)(myrand()%ar.X)+0.5,
2525 (float)(myrand_range(route_start_y_min, route_start_y_max))+0.5,
2526 (float)(myrand()%ar.Z)+0.5
2529 MapNode airnode(CONTENT_AIR);
2532 Generate some tunnel starting from orp
2535 for(u16 j=0; j<tunnel_routepoints; j++)
2537 if(j%7==0 && bruise_surface == false)
2539 main_direction = v3f(
2540 ((float)(myrand()%20)-(float)10)/10,
2541 ((float)(myrand()%20)-(float)10)/30,
2542 ((float)(myrand()%20)-(float)10)/10
2544 main_direction *= (float)myrand_range(1, 3);
2548 s16 min_d = min_tunnel_diameter;
2549 s16 max_d = max_tunnel_diameter;
2550 s16 rs = myrand_range(min_d, max_d);
2555 maxlen = v3s16(rs*7,rs*7,rs*7);
2559 maxlen = v3s16(rs*4, myrand_range(1, rs*3), rs*4);
2564 if(coming_from_surface && j < 3)
2567 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2568 (float)(myrand()%(maxlen.Y*1))-(float)maxlen.Y,
2569 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2575 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2576 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2577 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2581 vec += main_direction;
2586 else if(rp.X >= ar.X)
2588 if(rp.Y < route_y_min)
2590 else if(rp.Y >= route_y_max)
2591 rp.Y = route_y_max-1;
2594 else if(rp.Z >= ar.Z)
2598 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2600 v3f fp = orp + vec * f;
2601 v3s16 cp(fp.X, fp.Y, fp.Z);
2604 s16 d1 = d0 + rs - 1;
2605 for(s16 z0=d0; z0<=d1; z0++)
2607 //s16 si = rs - MYMAX(0, abs(z0)-rs/4);
2608 s16 si = rs - MYMAX(0, abs(z0)-rs/7);
2609 for(s16 x0=-si; x0<=si-1; x0++)
2611 s16 maxabsxz = MYMAX(abs(x0), abs(z0));
2612 //s16 si2 = rs - MYMAX(0, maxabsxz-rs/4);
2613 s16 si2 = rs - MYMAX(0, maxabsxz-rs/7);
2614 //s16 si2 = rs - abs(x0);
2615 for(s16 y0=-si2+1+2; y0<=si2-1; y0++)
2621 /*if(isInArea(p, ar) == false)
2623 // Check only height
2624 if(y < 0 || y >= ar.Y)
2628 //assert(data->vmanip.m_area.contains(p));
2629 if(data->vmanip.m_area.contains(p) == false)
2631 dstream<<"WARNING: "<<__FUNCTION_NAME
2632 <<":"<<__LINE__<<": "
2633 <<"point not in area"
2638 // Just set it to air, it will be changed to
2640 u32 i = data->vmanip.m_area.index(p);
2641 data->vmanip.m_data[i] = airnode;
2643 if(bruise_surface == false)
2646 data->vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON;
2664 //TimeTaker timer1("ore veins");
2669 for(u32 jj=0; jj<relative_volume/1000; jj++)
2671 s16 max_vein_diameter = 3;
2673 // Allowed route area size in nodes
2675 data->sectorpos_base_size*MAP_BLOCKSIZE,
2676 h_blocks*MAP_BLOCKSIZE,
2677 data->sectorpos_base_size*MAP_BLOCKSIZE
2680 // Area starting point in nodes
2682 data->sectorpos_base.X*MAP_BLOCKSIZE,
2683 data->y_blocks_min*MAP_BLOCKSIZE,
2684 data->sectorpos_base.Y*MAP_BLOCKSIZE
2688 //(this should be more than the maximum radius of the tunnel)
2690 s16 more = data->max_spread_amount - max_vein_diameter/2 - insure;
2691 ar += v3s16(1,0,1) * more * 2;
2692 of -= v3s16(1,0,1) * more;
2694 // Randomize starting position
2696 (float)(myrand()%ar.X)+0.5,
2697 (float)(myrand()%ar.Y)+0.5,
2698 (float)(myrand()%ar.Z)+0.5
2701 // Randomize mineral
2703 if(myrand()%4 != 0 || (orp.Y + of.Y) > 10)
2704 mineral = MINERAL_COAL;
2706 mineral = MINERAL_IRON;
2709 Generate some vein starting from orp
2712 for(u16 j=0; j<2; j++)
2715 (float)(myrand()%ar.X)+0.5,
2716 (float)(myrand()%ar.Y)+0.5,
2717 (float)(myrand()%ar.Z)+0.5
2719 v3f vec = rp - orp;*/
2721 v3s16 maxlen(5, 5, 5);
2723 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2724 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2725 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2730 else if(rp.X >= ar.X)
2734 else if(rp.Y >= ar.Y)
2738 else if(rp.Z >= ar.Z)
2744 s16 max_d = max_vein_diameter;
2745 s16 rs = myrand_range(min_d, max_d);
2747 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2749 v3f fp = orp + vec * f;
2750 v3s16 cp(fp.X, fp.Y, fp.Z);
2752 s16 d1 = d0 + rs - 1;
2753 for(s16 z0=d0; z0<=d1; z0++)
2755 s16 si = rs - abs(z0);
2756 for(s16 x0=-si; x0<=si-1; x0++)
2758 s16 si2 = rs - abs(x0);
2759 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2761 // Don't put mineral to every place
2769 /*if(isInArea(p, ar) == false)
2771 // Check only height
2772 if(y < 0 || y >= ar.Y)
2776 assert(data->vmanip.m_area.contains(p));
2778 // Just set it to air, it will be changed to
2780 u32 i = data->vmanip.m_area.index(p);
2781 MapNode *n = &data->vmanip.m_data[i];
2782 if(n->d == CONTENT_STONE)
2800 TimeTaker timer1("add mud");
2803 Add mud to the central chunk
2806 for(s16 x=0; x<data->sectorpos_base_size*MAP_BLOCKSIZE; x++)
2807 for(s16 z=0; z<data->sectorpos_base_size*MAP_BLOCKSIZE; z++)
2809 // Node position in 2d
2810 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2812 // Randomize mud amount
2813 s16 mud_add_amount = get_mud_add_amount(data->seed, p2d) / 2.0;
2815 // Find ground level
2816 s16 surface_y = find_ground_level_clever(data->vmanip, p2d);
2819 If topmost node is grass, change it to mud.
2820 It might be if it was flown to there from a neighboring
2821 chunk and then converted.
2824 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y));
2825 MapNode *n = &data->vmanip.m_data[i];
2826 if(n->d == CONTENT_GRASS)
2827 *n = MapNode(CONTENT_MUD);
2828 //n->d = CONTENT_MUD;
2836 v3s16 em = data->vmanip.m_area.getExtent();
2837 s16 y_start = surface_y+1;
2838 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2839 for(s16 y=y_start; y<=y_nodes_max; y++)
2841 if(mudcount >= mud_add_amount)
2844 MapNode &n = data->vmanip.m_data[i];
2845 n = MapNode(CONTENT_MUD);
2846 //n.d = CONTENT_MUD;
2849 data->vmanip.m_area.add_y(em, i, 1);
2861 TimeTaker timer1("flow mud");
2864 Flow mud away from steep edges
2867 // Limit area by 1 because mud is flown into neighbors.
2868 s16 mudflow_minpos = 0-data->max_spread_amount+1;
2869 s16 mudflow_maxpos = data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-2;
2871 // Iterate a few times
2872 for(s16 k=0; k<3; k++)
2875 for(s16 x=mudflow_minpos;
2878 for(s16 z=mudflow_minpos;
2882 // Invert coordinates every 2nd iteration
2885 x = mudflow_maxpos - (x-mudflow_minpos);
2886 z = mudflow_maxpos - (z-mudflow_minpos);
2889 // Node position in 2d
2890 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2892 v3s16 em = data->vmanip.m_area.getExtent();
2893 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2900 for(; y>=y_nodes_min; y--)
2902 n = &data->vmanip.m_data[i];
2903 //if(content_walkable(n->d))
2905 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2908 data->vmanip.m_area.add_y(em, i, -1);
2911 // Stop if out of area
2912 //if(data->vmanip.m_area.contains(i) == false)
2916 /*// If not mud, do nothing to it
2917 MapNode *n = &data->vmanip.m_data[i];
2918 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
2922 Don't flow it if the stuff under it is not mud
2926 data->vmanip.m_area.add_y(em, i2, -1);
2927 // Cancel if out of area
2928 if(data->vmanip.m_area.contains(i2) == false)
2930 MapNode *n2 = &data->vmanip.m_data[i2];
2931 if(n2->d != CONTENT_MUD && n2->d != CONTENT_GRASS)
2935 // Make it exactly mud
2938 /*s16 recurse_count = 0;
2942 v3s16(0,0,1), // back
2943 v3s16(1,0,0), // right
2944 v3s16(0,0,-1), // front
2945 v3s16(-1,0,0), // left
2948 // Theck that upper is air or doesn't exist.
2949 // Cancel dropping if upper keeps it in place
2951 data->vmanip.m_area.add_y(em, i3, 1);
2952 if(data->vmanip.m_area.contains(i3) == true
2953 && content_walkable(data->vmanip.m_data[i3].d) == true)
2960 for(u32 di=0; di<4; di++)
2962 v3s16 dirp = dirs4[di];
2965 data->vmanip.m_area.add_p(em, i2, dirp);
2966 // Fail if out of area
2967 if(data->vmanip.m_area.contains(i2) == false)
2969 // Check that side is air
2970 MapNode *n2 = &data->vmanip.m_data[i2];
2971 if(content_walkable(n2->d))
2973 // Check that under side is air
2974 data->vmanip.m_area.add_y(em, i2, -1);
2975 if(data->vmanip.m_area.contains(i2) == false)
2977 n2 = &data->vmanip.m_data[i2];
2978 if(content_walkable(n2->d))
2980 /*// Check that under that is air (need a drop of 2)
2981 data->vmanip.m_area.add_y(em, i2, -1);
2982 if(data->vmanip.m_area.contains(i2) == false)
2984 n2 = &data->vmanip.m_data[i2];
2985 if(content_walkable(n2->d))
2987 // Loop further down until not air
2989 data->vmanip.m_area.add_y(em, i2, -1);
2990 // Fail if out of area
2991 if(data->vmanip.m_area.contains(i2) == false)
2993 n2 = &data->vmanip.m_data[i2];
2994 }while(content_walkable(n2->d) == false);
2995 // Loop one up so that we're in air
2996 data->vmanip.m_area.add_y(em, i2, 1);
2997 n2 = &data->vmanip.m_data[i2];
2999 // Move mud to new place
3001 // Set old place to be air
3002 *n = MapNode(CONTENT_AIR);
3018 TimeTaker timer1("add water");
3021 Add water to the central chunk (and a bit more)
3024 for(s16 x=0-data->max_spread_amount;
3025 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3027 for(s16 z=0-data->max_spread_amount;
3028 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3031 // Node position in 2d
3032 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3034 // Find ground level
3035 //s16 surface_y = find_ground_level(data->vmanip, p2d);
3038 If ground level is over water level, skip.
3039 NOTE: This leaves caves near water without water,
3040 which looks especially crappy when the nearby water
3041 won't start flowing either for some reason
3043 /*if(surface_y > WATER_LEVEL)
3050 v3s16 em = data->vmanip.m_area.getExtent();
3051 u8 light = LIGHT_MAX;
3052 // Start at global water surface level
3053 s16 y_start = WATER_LEVEL;
3054 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3055 MapNode *n = &data->vmanip.m_data[i];
3057 for(s16 y=y_start; y>=y_nodes_min; y--)
3059 n = &data->vmanip.m_data[i];
3061 // Stop when there is no water and no air
3062 if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
3063 && n->d != CONTENT_WATER)
3069 // Make water only not in caves
3070 if(!(data->vmanip.m_flags[i]&VMANIP_FLAG_DUNGEON))
3072 n->d = CONTENT_WATERSOURCE;
3073 //n->setLight(LIGHTBANK_DAY, light);
3075 // Add to transforming liquid queue (in case it'd
3077 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3078 data->transforming_liquid.push_back(p);
3082 data->vmanip.m_area.add_y(em, i, -1);
3094 /***********************
3096 ************************/
3100 //TimeTaker timer1("convert mud to sand");
3106 //s16 mud_add_amount = myrand_range(2, 4);
3107 //s16 mud_add_amount = 0;
3109 /*for(s16 x=0; x<data->sectorpos_base_size*MAP_BLOCKSIZE; x++)
3110 for(s16 z=0; z<data->sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3111 for(s16 x=0-data->max_spread_amount+1;
3112 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3114 for(s16 z=0-data->max_spread_amount+1;
3115 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3118 // Node position in 2d
3119 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3121 // Determine whether to have sand here
3122 double sandnoise = noise2d_perlin(
3123 0.5+(float)p2d.X/500, 0.5+(float)p2d.Y/500,
3124 data->seed+59420, 3, 0.50);
3126 bool have_sand = (sandnoise > -0.15);
3128 if(have_sand == false)
3131 // Find ground level
3132 s16 surface_y = find_ground_level_clever(data->vmanip, p2d);
3134 if(surface_y > WATER_LEVEL + 2)
3138 v3s16 em = data->vmanip.m_area.getExtent();
3139 s16 y_start = surface_y;
3140 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3141 u32 not_sand_counter = 0;
3142 for(s16 y=y_start; y>=y_nodes_min; y--)
3144 MapNode *n = &data->vmanip.m_data[i];
3145 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
3147 n->d = CONTENT_SAND;
3152 if(not_sand_counter > 3)
3156 data->vmanip.m_area.add_y(em, i, -1);
3168 //TimeTaker timer1("generate trees");
3174 // Divide area into parts
3176 s16 sidelen = data->sectorpos_base_size*MAP_BLOCKSIZE / div;
3177 double area = sidelen * sidelen;
3178 for(s16 x0=0; x0<div; x0++)
3179 for(s16 z0=0; z0<div; z0++)
3181 // Center position of part of division
3183 data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen/2 + sidelen*x0,
3184 data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen/2 + sidelen*z0
3186 // Minimum edge of part of division
3188 data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen*x0,
3189 data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen*z0
3191 // Maximum edge of part of division
3193 data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen + sidelen*x0 - 1,
3194 data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen + sidelen*z0 - 1
3197 u32 tree_count = area * tree_amount_2d(data->seed, p2d_center);
3198 // Put trees in random places on part of division
3199 for(u32 i=0; i<tree_count; i++)
3201 s16 x = myrand_range(p2d_min.X, p2d_max.X);
3202 s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
3203 s16 y = find_ground_level(data->vmanip, v2s16(x,z));
3204 // Don't make a tree under water level
3207 // Don't make a tree so high that it doesn't fit
3208 if(y > y_nodes_max - 6)
3212 Trees grow only on mud and grass
3215 u32 i = data->vmanip.m_area.index(v3s16(p));
3216 MapNode *n = &data->vmanip.m_data[i];
3217 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
3222 make_tree(data->vmanip, p);
3225 /*u32 tree_max = relative_area / 60;
3226 //u32 count = myrand_range(0, tree_max);
3227 for(u32 i=0; i<count; i++)
3229 s16 x = myrand_range(0, data->sectorpos_base_size*MAP_BLOCKSIZE-1);
3230 s16 z = myrand_range(0, data->sectorpos_base_size*MAP_BLOCKSIZE-1);
3231 x += data->sectorpos_base.X*MAP_BLOCKSIZE;
3232 z += data->sectorpos_base.Y*MAP_BLOCKSIZE;
3233 s16 y = find_ground_level(data->vmanip, v2s16(x,z));
3234 // Don't make a tree under water level
3239 make_tree(data->vmanip, p);
3249 //TimeTaker timer1("grow grass");
3255 /*for(s16 x=0-4; x<data->sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
3256 for(s16 z=0-4; z<data->sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
3257 for(s16 x=0-data->max_spread_amount;
3258 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3260 for(s16 z=0-data->max_spread_amount;
3261 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3264 // Node position in 2d
3265 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3268 Find the lowest surface to which enough light ends up
3271 Basically just wait until not air and not leaves.
3275 v3s16 em = data->vmanip.m_area.getExtent();
3276 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
3278 // Go to ground level
3279 for(y=y_nodes_max; y>=y_nodes_min; y--)
3281 MapNode &n = data->vmanip.m_data[i];
3282 if(n.d != CONTENT_AIR
3283 && n.d != CONTENT_LEAVES)
3285 data->vmanip.m_area.add_y(em, i, -1);
3287 if(y >= y_nodes_min)
3290 surface_y = y_nodes_min;
3293 u32 i = data->vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
3294 MapNode *n = &data->vmanip.m_data[i];
3295 if(n->d == CONTENT_MUD)
3296 n->d = CONTENT_GRASS;
3303 Initial lighting (sunlight)
3306 core::map<v3s16, bool> light_sources;
3309 // 750ms @cs=8, can't optimize more
3310 TimeTaker timer1("initial lighting");
3312 // NOTE: This is no used... umm... for some reason!
3315 Go through the edges and add all nodes that have light to light_sources
3319 for(s16 i=0; i<4; i++)
3321 for(s16 j=lighting_min_d;
3328 if(i == 0 || i == 1)
3330 x = (i==0) ? lighting_min_d : lighting_max_d;
3339 z = (i==0) ? lighting_min_d : lighting_max_d;
3346 // Node position in 2d
3347 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3350 v3s16 em = data->vmanip.m_area.getExtent();
3351 s16 y_start = y_nodes_max;
3352 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3353 for(s16 y=y_start; y>=y_nodes_min; y--)
3355 MapNode *n = &data->vmanip.m_data[i];
3356 if(n->getLight(LIGHTBANK_DAY) != 0)
3358 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3360 //NOTE: This is broken, at least the index has to
3369 Go through the edges and apply sunlight to them, not caring
3374 for(s16 i=0; i<4; i++)
3376 for(s16 j=lighting_min_d;
3383 if(i == 0 || i == 1)
3385 x = (i==0) ? lighting_min_d : lighting_max_d;
3394 z = (i==0) ? lighting_min_d : lighting_max_d;
3401 // Node position in 2d
3402 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3404 // Loop from top to down
3406 u8 light = LIGHT_SUN;
3407 v3s16 em = data->vmanip.m_area.getExtent();
3408 s16 y_start = y_nodes_max;
3409 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3410 for(s16 y=y_start; y>=y_nodes_min; y--)
3412 MapNode *n = &data->vmanip.m_data[i];
3413 if(light_propagates_content(n->d) == false)
3417 else if(light != LIGHT_SUN
3418 || sunlight_propagates_content(n->d) == false)
3424 n->setLight(LIGHTBANK_DAY, light);
3425 n->setLight(LIGHTBANK_NIGHT, 0);
3429 // Insert light source
3430 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3433 // Increment index by y
3434 data->vmanip.m_area.add_y(em, i, -1);
3440 /*for(s16 x=0; x<data->sectorpos_base_size*MAP_BLOCKSIZE; x++)
3441 for(s16 z=0; z<data->sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3442 /*for(s16 x=0-data->max_spread_amount+1;
3443 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3445 for(s16 z=0-data->max_spread_amount+1;
3446 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3450 This has to be 1 smaller than the actual area, because
3451 neighboring nodes are checked.
3453 for(s16 x=lighting_min_d+1;
3454 x<=lighting_max_d-1;
3456 for(s16 z=lighting_min_d+1;
3457 z<=lighting_max_d-1;
3460 // Node position in 2d
3461 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3464 Apply initial sunlight
3467 u8 light = LIGHT_SUN;
3468 bool add_to_sources = false;
3469 v3s16 em = data->vmanip.m_area.getExtent();
3470 s16 y_start = y_nodes_max;
3471 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3472 for(s16 y=y_start; y>=y_nodes_min; y--)
3474 MapNode *n = &data->vmanip.m_data[i];
3476 if(light_propagates_content(n->d) == false)
3480 else if(light != LIGHT_SUN
3481 || sunlight_propagates_content(n->d) == false)
3487 // This doesn't take much time
3488 if(add_to_sources == false)
3491 Check sides. If side is not air or water, start
3492 adding to light_sources.
3495 v3s16(0,0,1), // back
3496 v3s16(1,0,0), // right
3497 v3s16(0,0,-1), // front
3498 v3s16(-1,0,0), // left
3500 for(u32 di=0; di<4; di++)
3502 v3s16 dirp = dirs4[di];
3504 data->vmanip.m_area.add_p(em, i2, dirp);
3505 MapNode *n2 = &data->vmanip.m_data[i2];
3507 n2->d != CONTENT_AIR
3508 && n2->d != CONTENT_WATERSOURCE
3509 && n2->d != CONTENT_WATER
3511 add_to_sources = true;
3517 n->setLight(LIGHTBANK_DAY, light);
3518 n->setLight(LIGHTBANK_NIGHT, 0);
3520 // This doesn't take much time
3521 if(light != 0 && add_to_sources)
3523 // Insert light source
3524 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3527 // Increment index by y
3528 data->vmanip.m_area.add_y(em, i, -1);
3536 // Spread light around
3538 TimeTaker timer("makeChunk() spreadLight");
3539 data->vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
3546 timer_generate.stop();
3549 //###################################################################
3550 //###################################################################
3551 //###################################################################
3552 //###################################################################
3553 //###################################################################
3554 //###################################################################
3555 //###################################################################
3556 //###################################################################
3557 //###################################################################
3558 //###################################################################
3559 //###################################################################
3560 //###################################################################
3561 //###################################################################
3562 //###################################################################
3563 //###################################################################
3565 void ServerMap::initChunkMake(ChunkMakeData &data, v2s16 chunkpos)
3567 if(m_chunksize == 0)
3575 // The distance how far into the neighbors the generator is allowed to go.
3576 s16 max_spread_amount_sectors = 2;
3577 assert(max_spread_amount_sectors <= m_chunksize);
3578 s16 max_spread_amount = max_spread_amount_sectors * MAP_BLOCKSIZE;
3580 s16 y_blocks_min = -4;
3581 s16 y_blocks_max = 3;
3583 v2s16 sectorpos_base = chunk_to_sector(chunkpos);
3584 s16 sectorpos_base_size = m_chunksize;
3586 v2s16 sectorpos_bigbase =
3587 sectorpos_base - v2s16(1,1) * max_spread_amount_sectors;
3588 s16 sectorpos_bigbase_size =
3589 sectorpos_base_size + 2 * max_spread_amount_sectors;
3592 const s16 limit = MAP_GENERATION_LIMIT / MAP_BLOCKSIZE;
3593 if(sectorpos_bigbase.X < -limit
3594 || sectorpos_bigbase.X + sectorpos_bigbase_size >= limit
3595 || sectorpos_bigbase.Y < -limit
3596 || sectorpos_bigbase.Y + sectorpos_bigbase_size >= limit)
3603 data.chunkpos = chunkpos;
3604 data.y_blocks_min = y_blocks_min;
3605 data.y_blocks_max = y_blocks_max;
3606 data.sectorpos_base = sectorpos_base;
3607 data.sectorpos_base_size = sectorpos_base_size;
3608 data.sectorpos_bigbase = sectorpos_bigbase;
3609 data.sectorpos_bigbase_size = sectorpos_bigbase_size;
3610 data.max_spread_amount = max_spread_amount;
3613 Create the whole area of this and the neighboring chunks
3616 TimeTaker timer("initChunkMake() create area");
3618 for(s16 x=0; x<sectorpos_bigbase_size; x++)
3619 for(s16 z=0; z<sectorpos_bigbase_size; z++)
3621 v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
3622 ServerMapSector *sector = createSector(sectorpos);
3625 for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
3627 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
3628 MapBlock *block = createBlock(blockpos);
3630 // Lighting won't be calculated
3631 //block->setLightingExpired(true);
3632 // Lighting will be calculated
3633 block->setLightingExpired(false);
3636 Block gets sunlight if this is true.
3638 This should be set to true when the top side of a block
3639 is completely exposed to the sky.
3641 Actually this doesn't matter now because the
3642 initial lighting is done here.
3644 block->setIsUnderground(y != y_blocks_max);
3650 Now we have a big empty area.
3652 Make a ManualMapVoxelManipulator that contains this and the
3656 v3s16 bigarea_blocks_min(
3657 sectorpos_bigbase.X,
3661 v3s16 bigarea_blocks_max(
3662 sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
3664 sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
3667 data.vmanip.setMap(this);
3670 TimeTaker timer("initChunkMake() initialEmerge");
3671 data.vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
3676 MapChunk* ServerMap::finishChunkMake(ChunkMakeData &data,
3677 core::map<v3s16, MapBlock*> &changed_blocks)
3683 Blit generated stuff to map
3687 //TimeTaker timer("generateChunkRaw() blitBackAll");
3688 data.vmanip.blitBackAll(&changed_blocks);
3692 Update day/night difference cache of the MapBlocks
3695 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
3696 i.atEnd() == false; i++)
3698 MapBlock *block = i.getNode()->getValue();
3699 block->updateDayNightDiff();
3704 Copy transforming liquid information
3706 while(data.transforming_liquid.size() > 0)
3708 v3s16 p = data.transforming_liquid.pop_front();
3709 m_transforming_liquid.push_back(p);
3713 Add random objects to blocks
3716 for(s16 x=0; x<data.sectorpos_base_size; x++)
3717 for(s16 z=0; z<data.sectorpos_base_size; z++)
3719 v2s16 sectorpos = data.sectorpos_base + v2s16(x,z);
3720 ServerMapSector *sector = createSector(sectorpos);
3723 for(s16 y=data.y_blocks_min; y<=data.y_blocks_max; y++)
3725 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
3726 MapBlock *block = createBlock(blockpos);
3727 addRandomObjects(block);
3733 Create chunk metadata
3736 for(s16 x=-1; x<=1; x++)
3737 for(s16 y=-1; y<=1; y++)
3739 v2s16 chunkpos0 = data.chunkpos + v2s16(x,y);
3740 // Add chunk meta information
3741 MapChunk *chunk = getChunk(chunkpos0);
3744 chunk = new MapChunk();
3745 m_chunks.insert(chunkpos0, chunk);
3747 //chunk->setIsVolatile(true);
3748 if(chunk->getGenLevel() > GENERATED_PARTLY)
3749 chunk->setGenLevel(GENERATED_PARTLY);
3753 Set central chunk non-volatile
3755 MapChunk *chunk = getChunk(data.chunkpos);
3758 //chunk->setIsVolatile(false);
3759 chunk->setGenLevel(GENERATED_FULLY);
3762 Save changed parts of map
3771 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
3772 core::map<v3s16, MapBlock*> &changed_blocks,
3775 DSTACK(__FUNCTION_NAME);
3778 Don't generate if already fully generated
3782 MapChunk *chunk = getChunk(chunkpos);
3783 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3785 dstream<<"generateChunkRaw(): Chunk "
3786 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
3787 <<" already generated"<<std::endl;
3792 dstream<<"generateChunkRaw(): Generating chunk "
3793 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
3796 TimeTaker timer("generateChunkRaw()");
3800 // Initialize generation
3801 initChunkMake(data, chunkpos);
3806 // Finalize generation
3807 MapChunk *chunk = finishChunkMake(data, changed_blocks);
3810 Return central chunk (which was requested)
3816 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
3817 core::map<v3s16, MapBlock*> &changed_blocks)
3819 dstream<<"generateChunk(): Generating chunk "
3820 <<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
3823 /*for(s16 x=-1; x<=1; x++)
3824 for(s16 y=-1; y<=1; y++)*/
3825 for(s16 x=-0; x<=0; x++)
3826 for(s16 y=-0; y<=0; y++)
3828 v2s16 chunkpos0 = chunkpos1 + v2s16(x,y);
3829 MapChunk *chunk = getChunk(chunkpos0);
3830 // Skip if already generated
3831 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3833 generateChunkRaw(chunkpos0, changed_blocks);
3836 assert(chunkNonVolatile(chunkpos1));
3838 MapChunk *chunk = getChunk(chunkpos1);
3843 ServerMapSector * ServerMap::createSector(v2s16 p2d)
3845 DSTACKF("%s: p2d=(%d,%d)",
3850 Check if it exists already in memory
3852 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3857 Try to load it from disk (with blocks)
3859 if(loadSectorFull(p2d) == true)
3861 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3864 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
3865 throw InvalidPositionException("");
3871 Do not create over-limit
3873 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3874 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3875 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3876 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3877 throw InvalidPositionException("createSector(): pos. over limit");
3880 Generate blank sector
3883 sector = new ServerMapSector(this, p2d);
3885 // Sector position on map in nodes
3886 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3891 m_sectors.insert(p2d, sector);
3897 MapSector * ServerMap::emergeSector(v2s16 p2d,
3898 core::map<v3s16, MapBlock*> &changed_blocks)
3900 DSTACK("%s: p2d=(%d,%d)",
3907 v2s16 chunkpos = sector_to_chunk(p2d);
3908 /*bool chunk_nonvolatile = false;
3909 MapChunk *chunk = getChunk(chunkpos);
3910 if(chunk && chunk->getIsVolatile() == false)
3911 chunk_nonvolatile = true;*/
3912 bool chunk_nonvolatile = chunkNonVolatile(chunkpos);
3915 If chunk is not fully generated, generate chunk
3917 if(chunk_nonvolatile == false)
3919 // Generate chunk and neighbors
3920 generateChunk(chunkpos, changed_blocks);
3924 Return sector if it exists now
3926 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3931 Try to load it from disk
3933 if(loadSectorFull(p2d) == true)
3935 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3938 dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
3939 throw InvalidPositionException("");
3945 generateChunk should have generated the sector
3949 dstream<<"WARNING: ServerMap::emergeSector: Cannot find sector ("
3950 <<p2d.X<<","<<p2d.Y<<" and chunk is already generated. "
3954 dstream<<"WARNING: Creating an empty sector."<<std::endl;
3956 return createSector(p2d);
3961 dstream<<"WARNING: Forcing regeneration of chunk."<<std::endl;
3964 generateChunkRaw(chunkpos, changed_blocks, true);
3967 Return sector if it exists now
3969 sector = getSectorNoGenerateNoEx(p2d);
3973 dstream<<"ERROR: Could not get sector from anywhere."<<std::endl;
3981 //return generateSector();
3986 NOTE: This is not used for main map generation, only for blocks
3987 that are very high or low
3989 MapBlock * ServerMap::generateBlock(
3991 MapBlock *original_dummy,
3992 ServerMapSector *sector,
3993 core::map<v3s16, MapBlock*> &changed_blocks,
3994 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
3997 DSTACKF("%s: p=(%d,%d,%d)",
4001 // If chunks are disabled
4002 /*if(m_chunksize == 0)
4004 dstream<<"ServerMap::generateBlock(): Chunks disabled -> "
4005 <<"not generating."<<std::endl;
4009 /*dstream<<"generateBlock(): "
4010 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4013 MapBlock *block = original_dummy;
4015 v2s16 p2d(p.X, p.Z);
4017 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
4020 Do not generate over-limit
4022 if(blockpos_over_limit(p))
4024 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
4025 throw InvalidPositionException("generateBlock(): pos. over limit");
4029 If block doesn't exist, create one.
4030 If it exists, it is a dummy. In that case unDummify() it.
4032 NOTE: This already sets the map as the parent of the block
4036 block = sector->createBlankBlockNoInsert(block_y);
4040 // Remove the block so that nobody can get a half-generated one.
4041 sector->removeBlock(block);
4042 // Allocate the block to contain the generated data
4048 Generate a completely empty block
4050 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4051 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4053 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4057 block->setNode(v3s16(x0,y0,z0), n);
4062 Generate a proper block
4065 u8 water_material = CONTENT_WATERSOURCE;
4067 s32 lowest_ground_y = 32767;
4068 s32 highest_ground_y = -32768;
4070 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4071 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4073 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
4075 //s16 surface_y = 0;
4077 s16 mud_add_amount = get_mud_add_amount(m_seed, p2d_nodes+v2s16(x0,z0));
4079 s16 surface_y = base_rock_level_2d(m_seed, p2d_nodes+v2s16(x0,z0))
4081 // If chunks are disabled
4082 if(m_chunksize == 0)
4083 surface_y = WATER_LEVEL + 1;
4085 if(surface_y < lowest_ground_y)
4086 lowest_ground_y = surface_y;
4087 if(surface_y > highest_ground_y)
4088 highest_ground_y = surface_y;
4090 s32 surface_depth = AVERAGE_MUD_AMOUNT;
4092 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4094 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
4099 NOTE: If there are some man-made structures above the
4100 newly created block, they won't be taken into account.
4102 if(real_y > surface_y)
4103 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
4109 // If node is over heightmap y, it's air or water
4110 if(real_y > surface_y)
4112 // If under water level, it's water
4113 if(real_y <= WATER_LEVEL)
4115 n.d = water_material;
4116 n.setLight(LIGHTBANK_DAY,
4117 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
4119 Add to transforming liquid queue (in case it'd
4122 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
4123 m_transforming_liquid.push_back(real_pos);
4129 // Else it's ground or caves (air)
4132 // If it's surface_depth under ground, it's stone
4133 if(real_y <= surface_y - surface_depth)
4135 n.d = CONTENT_STONE;
4139 // It is mud if it is under the first ground
4140 // level or under water
4141 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
4147 n.d = CONTENT_GRASS;
4150 //n.d = CONTENT_MUD;
4152 /*// If under water level, it's mud
4153 if(real_y < WATER_LEVEL)
4155 // Only the topmost node is grass
4156 else if(real_y <= surface_y - 1)
4159 n.d = CONTENT_GRASS;*/
4163 block->setNode(v3s16(x0,y0,z0), n);
4168 Calculate some helper variables
4171 // Completely underground if the highest part of block is under lowest
4173 // This has to be very sure; it's probably one too strict now but
4174 // that's just better.
4175 bool completely_underground =
4176 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
4178 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
4180 bool mostly_underwater_surface = false;
4181 if(highest_ground_y < WATER_LEVEL
4182 && some_part_underground && !completely_underground)
4183 mostly_underwater_surface = true;
4186 Get local attributes
4189 //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
4191 float caves_amount = 0.5;
4196 NOTE: BEWARE: Too big amount of attribute points slows verything
4198 1 interpolation from 5000 points takes 2-3ms.
4200 //TimeTaker timer("generateBlock() local attribute retrieval");
4201 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
4202 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
4203 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
4207 //dstream<<"generateBlock(): Done"<<std::endl;
4213 // Initialize temporary table
4214 const s32 ued = MAP_BLOCKSIZE;
4215 bool underground_emptiness[ued*ued*ued];
4216 for(s32 i=0; i<ued*ued*ued; i++)
4218 underground_emptiness[i] = 0;
4225 Initialize orp and ors. Try to find if some neighboring
4226 MapBlock has a tunnel ended in its side
4230 (float)(myrand()%ued)+0.5,
4231 (float)(myrand()%ued)+0.5,
4232 (float)(myrand()%ued)+0.5
4235 bool found_existing = false;
4241 for(s16 y=0; y<ued; y++)
4242 for(s16 x=0; x<ued; x++)
4244 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4245 if(getNode(ap).d == CONTENT_AIR)
4247 orp = v3f(x+1,y+1,0);
4248 found_existing = true;
4249 goto continue_generating;
4253 catch(InvalidPositionException &e){}
4259 for(s16 y=0; y<ued; y++)
4260 for(s16 x=0; x<ued; x++)
4262 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4263 if(getNode(ap).d == CONTENT_AIR)
4265 orp = v3f(x+1,y+1,ued-1);
4266 found_existing = true;
4267 goto continue_generating;
4271 catch(InvalidPositionException &e){}
4277 for(s16 y=0; y<ued; y++)
4278 for(s16 z=0; z<ued; z++)
4280 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4281 if(getNode(ap).d == CONTENT_AIR)
4283 orp = v3f(0,y+1,z+1);
4284 found_existing = true;
4285 goto continue_generating;
4289 catch(InvalidPositionException &e){}
4295 for(s16 y=0; y<ued; y++)
4296 for(s16 z=0; z<ued; z++)
4298 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4299 if(getNode(ap).d == CONTENT_AIR)
4301 orp = v3f(ued-1,y+1,z+1);
4302 found_existing = true;
4303 goto continue_generating;
4307 catch(InvalidPositionException &e){}
4313 for(s16 x=0; x<ued; x++)
4314 for(s16 z=0; z<ued; z++)
4316 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4317 if(getNode(ap).d == CONTENT_AIR)
4319 orp = v3f(x+1,0,z+1);
4320 found_existing = true;
4321 goto continue_generating;
4325 catch(InvalidPositionException &e){}
4331 for(s16 x=0; x<ued; x++)
4332 for(s16 z=0; z<ued; z++)
4334 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4335 if(getNode(ap).d == CONTENT_AIR)
4337 orp = v3f(x+1,ued-1,z+1);
4338 found_existing = true;
4339 goto continue_generating;
4343 catch(InvalidPositionException &e){}
4345 continue_generating:
4348 Choose whether to actually generate cave
4350 bool do_generate_caves = true;
4351 // Don't generate if no part is underground
4352 if(!some_part_underground)
4354 do_generate_caves = false;
4356 // Don't generate if mostly underwater surface
4357 /*else if(mostly_underwater_surface)
4359 do_generate_caves = false;
4361 // Partly underground = cave
4362 else if(!completely_underground)
4364 do_generate_caves = (rand() % 100 <= (s32)(caves_amount*100));
4366 // Found existing cave underground
4367 else if(found_existing && completely_underground)
4369 do_generate_caves = (rand() % 100 <= (s32)(caves_amount*100));
4371 // Underground and no caves found
4374 do_generate_caves = (rand() % 300 <= (s32)(caves_amount*100));
4377 if(do_generate_caves)
4380 Generate some tunnel starting from orp and ors
4382 for(u16 i=0; i<3; i++)
4385 (float)(myrand()%ued)+0.5,
4386 (float)(myrand()%ued)+0.5,
4387 (float)(myrand()%ued)+0.5
4391 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
4395 for(float f=0; f<1.0; f+=0.04)
4397 v3f fp = orp + vec * f;
4398 v3s16 cp(fp.X, fp.Y, fp.Z);
4400 s16 d1 = d0 + rs - 1;
4401 for(s16 z0=d0; z0<=d1; z0++)
4403 s16 si = rs - abs(z0);
4404 for(s16 x0=-si; x0<=si-1; x0++)
4406 s16 si2 = rs - abs(x0);
4407 for(s16 y0=-si2+1; y0<=si2-1; y0++)
4413 if(isInArea(p, ued) == false)
4415 underground_emptiness[ued*ued*z + ued*y + x] = 1;
4427 // Set to true if has caves.
4428 // Set when some non-air is changed to air when making caves.
4429 bool has_caves = false;
4432 Apply temporary cave data to block
4435 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4436 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4438 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4440 MapNode n = block->getNode(v3s16(x0,y0,z0));
4443 if(underground_emptiness[
4444 ued*ued*(z0*ued/MAP_BLOCKSIZE)
4445 +ued*(y0*ued/MAP_BLOCKSIZE)
4446 +(x0*ued/MAP_BLOCKSIZE)])
4448 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
4457 block->setNode(v3s16(x0,y0,z0), n);
4462 This is used for guessing whether or not the block should
4463 receive sunlight from the top if the block above doesn't exist
4465 block->setIsUnderground(completely_underground);
4468 Force lighting update if some part of block is partly
4469 underground and has caves.
4471 /*if(some_part_underground && !completely_underground && has_caves)
4473 //dstream<<"Half-ground caves"<<std::endl;
4474 lighting_invalidated_blocks[block->getPos()] = block;
4477 // DEBUG: Always update lighting
4478 //lighting_invalidated_blocks[block->getPos()] = block;
4484 if(some_part_underground)
4486 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
4491 for(s16 i=0; i<underground_level/4 + 1; i++)
4493 if(myrand()%50 == 0)
4496 (myrand()%(MAP_BLOCKSIZE-2))+1,
4497 (myrand()%(MAP_BLOCKSIZE-2))+1,
4498 (myrand()%(MAP_BLOCKSIZE-2))+1
4504 for(u16 i=0; i<27; i++)
4506 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4508 block->setNode(cp+g_27dirs[i], n);
4516 u16 coal_amount = 30;
4517 u16 coal_rareness = 60 / coal_amount;
4518 if(coal_rareness == 0)
4520 if(myrand()%coal_rareness == 0)
4522 u16 a = myrand() % 16;
4523 u16 amount = coal_amount * a*a*a / 1000;
4524 for(s16 i=0; i<amount; i++)
4527 (myrand()%(MAP_BLOCKSIZE-2))+1,
4528 (myrand()%(MAP_BLOCKSIZE-2))+1,
4529 (myrand()%(MAP_BLOCKSIZE-2))+1
4533 n.d = CONTENT_STONE;
4534 n.param = MINERAL_COAL;
4536 for(u16 i=0; i<27; i++)
4538 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4540 block->setNode(cp+g_27dirs[i], n);
4548 u16 iron_amount = 8;
4549 u16 iron_rareness = 60 / iron_amount;
4550 if(iron_rareness == 0)
4552 if(myrand()%iron_rareness == 0)
4554 u16 a = myrand() % 16;
4555 u16 amount = iron_amount * a*a*a / 1000;
4556 for(s16 i=0; i<amount; i++)
4559 (myrand()%(MAP_BLOCKSIZE-2))+1,
4560 (myrand()%(MAP_BLOCKSIZE-2))+1,
4561 (myrand()%(MAP_BLOCKSIZE-2))+1
4565 n.d = CONTENT_STONE;
4566 n.param = MINERAL_IRON;
4568 for(u16 i=0; i<27; i++)
4570 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4572 block->setNode(cp+g_27dirs[i], n);
4579 Create a few rats in empty blocks underground
4581 if(completely_underground)
4583 //for(u16 i=0; i<2; i++)
4586 (myrand()%(MAP_BLOCKSIZE-2))+1,
4587 (myrand()%(MAP_BLOCKSIZE-2))+1,
4588 (myrand()%(MAP_BLOCKSIZE-2))+1
4591 // Check that the place is empty
4592 //if(!is_ground_content(block->getNode(cp).d))
4595 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp, BS));
4596 block->addObject(obj);
4601 #endif // end of proper block generation
4604 Add block to sector.
4606 sector->insertBlock(block);
4608 // Lighting is invalid after generation.
4609 block->setLightingExpired(true);
4616 <<"lighting_invalidated_blocks.size()"
4620 <<" "<<lighting_invalidated_blocks.size()
4622 <<", "<<completely_underground
4623 <<", "<<some_part_underground
4630 MapBlock * ServerMap::createBlock(v3s16 p)
4632 DSTACKF("%s: p=(%d,%d,%d)",
4633 __FUNCTION_NAME, p.X, p.Y, p.Z);
4636 Do not create over-limit
4638 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4639 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4640 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4641 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4642 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4643 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4644 throw InvalidPositionException("createBlock(): pos. over limit");
4646 v2s16 p2d(p.X, p.Z);
4649 This will create or load a sector if not found in memory.
4650 If block exists on disk, it will be loaded.
4652 NOTE: On old save formats, this will be slow, as it generates
4653 lighting on blocks for them.
4655 ServerMapSector *sector;
4657 sector = (ServerMapSector*)createSector(p2d);
4658 assert(sector->getId() == MAPSECTOR_SERVER);
4660 catch(InvalidPositionException &e)
4662 dstream<<"createBlock: createSector() failed"<<std::endl;
4666 NOTE: This should not be done, or at least the exception
4667 should not be passed on as std::exception, because it
4668 won't be catched at all.
4670 /*catch(std::exception &e)
4672 dstream<<"createBlock: createSector() failed: "
4673 <<e.what()<<std::endl;
4678 Try to get a block from the sector
4681 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4685 block = sector->createBlankBlock(block_y);
4689 MapBlock * ServerMap::emergeBlock(
4691 bool only_from_disk,
4692 core::map<v3s16, MapBlock*> &changed_blocks,
4693 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4696 DSTACKF("%s: p=(%d,%d,%d), only_from_disk=%d",
4698 p.X, p.Y, p.Z, only_from_disk);
4701 Do not generate over-limit
4703 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4704 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4705 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4706 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4707 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4708 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4709 throw InvalidPositionException("emergeBlock(): pos. over limit");
4711 v2s16 p2d(p.X, p.Z);
4714 This will create or load a sector if not found in memory.
4715 If block exists on disk, it will be loaded.
4717 ServerMapSector *sector;
4719 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
4720 assert(sector->getId() == MAPSECTOR_SERVER);
4722 catch(InvalidPositionException &e)
4724 dstream<<"emergeBlock: emergeSector() failed: "
4725 <<e.what()<<std::endl;
4726 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4728 <<"You could try to delete it."<<std::endl;
4731 catch(VersionMismatchException &e)
4733 dstream<<"emergeBlock: emergeSector() failed: "
4734 <<e.what()<<std::endl;
4735 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4737 <<"You could try to delete it."<<std::endl;
4741 NOTE: This should not be done, or at least the exception
4742 should not be passed on as std::exception, because it
4743 won't be catched at all.
4745 /*catch(std::exception &e)
4747 dstream<<"emergeBlock: emergeSector() failed: "
4748 <<e.what()<<std::endl;
4749 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4751 <<"You could try to delete it."<<std::endl;
4756 Try to get a block from the sector
4759 bool does_not_exist = false;
4760 bool lighting_expired = false;
4761 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4765 does_not_exist = true;
4767 else if(block->isDummy() == true)
4769 does_not_exist = true;
4771 else if(block->getLightingExpired())
4773 lighting_expired = true;
4778 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
4783 If block was not found on disk and not going to generate a
4784 new one, make sure there is a dummy block in place.
4786 if(only_from_disk && (does_not_exist || lighting_expired))
4788 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
4792 // Create dummy block
4793 block = new MapBlock(this, p, true);
4795 // Add block to sector
4796 sector->insertBlock(block);
4802 //dstream<<"Not found on disk, generating."<<std::endl;
4804 //TimeTaker("emergeBlock() generate");
4806 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
4809 If the block doesn't exist, generate the block.
4813 block = generateBlock(p, block, sector, changed_blocks,
4814 lighting_invalidated_blocks);
4817 if(lighting_expired)
4819 lighting_invalidated_blocks.insert(p, block);
4823 Initially update sunlight
4827 core::map<v3s16, bool> light_sources;
4828 bool black_air_left = false;
4829 bool bottom_invalid =
4830 block->propagateSunlight(light_sources, true,
4833 // If sunlight didn't reach everywhere and part of block is
4834 // above ground, lighting has to be properly updated
4835 //if(black_air_left && some_part_underground)
4838 lighting_invalidated_blocks[block->getPos()] = block;
4843 lighting_invalidated_blocks[block->getPos()] = block;
4850 s16 ServerMap::findGroundLevel(v2s16 p2d)
4853 Uh, just do something random...
4855 // Find existing map from top to down
4858 v3s16 p(p2d.X, max, p2d.Y);
4859 for(; p.Y>min; p.Y--)
4861 MapNode n = getNodeNoEx(p);
4862 if(n.d != CONTENT_IGNORE)
4867 // If this node is not air, go to plan b
4868 if(getNodeNoEx(p).d != CONTENT_AIR)
4870 // Search existing walkable and return it
4871 for(; p.Y>min; p.Y--)
4873 MapNode n = getNodeNoEx(p);
4874 if(content_walkable(n.d) && n.d != CONTENT_IGNORE)
4880 Plan B: Get from map generator perlin noise function
4882 // This won't work if proper generation is disabled
4883 if(m_chunksize == 0)
4884 return WATER_LEVEL+2;
4885 double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
4889 void ServerMap::createDirs(std::string path)
4891 if(fs::CreateAllDirs(path) == false)
4893 m_dout<<DTIME<<"ServerMap: Failed to create directory "
4894 <<"\""<<path<<"\""<<std::endl;
4895 throw BaseException("ServerMap failed to create directory");
4899 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
4905 snprintf(cc, 9, "%.4x%.4x",
4906 (unsigned int)pos.X&0xffff,
4907 (unsigned int)pos.Y&0xffff);
4909 return m_savedir + "/sectors/" + cc;
4911 snprintf(cc, 9, "%.3x/%.3x",
4912 (unsigned int)pos.X&0xfff,
4913 (unsigned int)pos.Y&0xfff);
4915 return m_savedir + "/sectors2/" + cc;
4921 v2s16 ServerMap::getSectorPos(std::string dirname)
4925 size_t spos = dirname.rfind('/') + 1;
4926 assert(spos != std::string::npos);
4927 if(dirname.size() - spos == 8)
4930 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
4932 else if(dirname.size() - spos == 3)
4935 r = sscanf(dirname.substr(spos-4).c_str(), "%3x/%3x", &x, &y);
4936 // Sign-extend the 12 bit values up to 16 bits...
4937 if(x&0x800) x|=0xF000;
4938 if(y&0x800) y|=0xF000;
4945 v2s16 pos((s16)x, (s16)y);
4949 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
4951 v2s16 p2d = getSectorPos(sectordir);
4953 if(blockfile.size() != 4){
4954 throw InvalidFilenameException("Invalid block filename");
4957 int r = sscanf(blockfile.c_str(), "%4x", &y);
4959 throw InvalidFilenameException("Invalid block filename");
4960 return v3s16(p2d.X, y, p2d.Y);
4963 void ServerMap::save(bool only_changed)
4965 DSTACK(__FUNCTION_NAME);
4966 if(m_map_saving_enabled == false)
4968 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
4972 if(only_changed == false)
4973 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
4976 if(only_changed == false || m_map_metadata_changed)
4981 // Disable saving chunk metadata if chunks are disabled
4982 if(m_chunksize != 0)
4984 if(only_changed == false || anyChunkModified())
4988 u32 sector_meta_count = 0;
4989 u32 block_count = 0;
4990 u32 block_count_all = 0; // Number of blocks in memory
4992 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
4993 for(; i.atEnd() == false; i++)
4995 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
4996 assert(sector->getId() == MAPSECTOR_SERVER);
4998 if(sector->differs_from_disk || only_changed == false)
5000 saveSectorMeta(sector);
5001 sector_meta_count++;
5003 core::list<MapBlock*> blocks;
5004 sector->getBlocks(blocks);
5005 core::list<MapBlock*>::Iterator j;
5006 for(j=blocks.begin(); j!=blocks.end(); j++)
5008 MapBlock *block = *j;
5012 if(block->getChangedFlag() || only_changed == false)
5017 /*dstream<<"ServerMap: Written block ("
5018 <<block->getPos().X<<","
5019 <<block->getPos().Y<<","
5020 <<block->getPos().Z<<")"
5027 Only print if something happened or saved whole map
5029 if(only_changed == false || sector_meta_count != 0
5030 || block_count != 0)
5032 dstream<<DTIME<<"ServerMap: Written: "
5033 <<sector_meta_count<<" sector metadata files, "
5034 <<block_count<<" block files"
5035 <<", "<<block_count_all<<" blocks in memory."
5041 // NOTE: Doing this is insane. Deprecated and probably broken.
5042 void ServerMap::loadAll()
5044 DSTACK(__FUNCTION_NAME);
5045 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
5050 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
5052 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
5054 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5057 s32 printed_counter = -100000;
5058 s32 count = list.size();
5060 std::vector<fs::DirListNode>::iterator i;
5061 for(i=list.begin(); i!=list.end(); i++)
5063 if(counter > printed_counter + 10)
5065 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
5066 printed_counter = counter;
5070 MapSector *sector = NULL;
5072 // We want directories
5076 sector = loadSectorMeta(i->name);
5078 catch(InvalidFilenameException &e)
5080 // This catches unknown crap in directory
5083 std::vector<fs::DirListNode> list2 = fs::GetDirListing
5084 (m_savedir+"/sectors/"+i->name);
5085 std::vector<fs::DirListNode>::iterator i2;
5086 for(i2=list2.begin(); i2!=list2.end(); i2++)
5092 loadBlock(i->name, i2->name, sector);
5094 catch(InvalidFilenameException &e)
5096 // This catches unknown crap in directory
5100 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
5105 void ServerMap::saveMasterHeightmap()
5107 DSTACK(__FUNCTION_NAME);
5109 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
5111 createDir(m_savedir);
5113 /*std::string fullpath = m_savedir + "/master_heightmap";
5114 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5115 if(o.good() == false)
5116 throw FileNotGoodException("Cannot open master heightmap");*/
5118 // Format used for writing
5119 //u8 version = SER_FMT_VER_HIGHEST;
5122 void ServerMap::loadMasterHeightmap()
5124 DSTACK(__FUNCTION_NAME);
5126 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
5128 /*std::string fullpath = m_savedir + "/master_heightmap";
5129 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5130 if(is.good() == false)
5131 throw FileNotGoodException("Cannot open master heightmap");*/
5135 void ServerMap::saveMapMeta()
5137 DSTACK(__FUNCTION_NAME);
5139 dstream<<"INFO: ServerMap::saveMapMeta(): "
5140 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
5143 createDirs(m_savedir);
5145 std::string fullpath = m_savedir + "/map_meta.txt";
5146 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
5147 if(os.good() == false)
5149 dstream<<"ERROR: ServerMap::saveMapMeta(): "
5150 <<"could not open"<<fullpath<<std::endl;
5151 throw FileNotGoodException("Cannot open chunk metadata");
5155 params.setU64("seed", m_seed);
5156 params.setS32("chunksize", m_chunksize);
5158 params.writeLines(os);
5160 os<<"[end_of_params]\n";
5162 m_map_metadata_changed = false;
5165 void ServerMap::loadMapMeta()
5167 DSTACK(__FUNCTION_NAME);
5169 dstream<<"INFO: ServerMap::loadMapMeta(): Loading map metadata"
5172 std::string fullpath = m_savedir + "/map_meta.txt";
5173 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5174 if(is.good() == false)
5176 dstream<<"ERROR: ServerMap::loadMapMeta(): "
5177 <<"could not open"<<fullpath<<std::endl;
5178 throw FileNotGoodException("Cannot open map metadata");
5186 throw SerializationError
5187 ("ServerMap::loadMapMeta(): [end_of_params] not found");
5189 std::getline(is, line);
5190 std::string trimmedline = trim(line);
5191 if(trimmedline == "[end_of_params]")
5193 params.parseConfigLine(line);
5196 m_seed = params.getU64("seed");
5197 m_chunksize = params.getS32("chunksize");
5199 dstream<<"INFO: ServerMap::loadMapMeta(): "
5200 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
5204 void ServerMap::saveChunkMeta()
5206 DSTACK(__FUNCTION_NAME);
5208 // This should not be called if chunks are disabled.
5209 assert(m_chunksize != 0);
5211 u32 count = m_chunks.size();
5213 dstream<<"INFO: ServerMap::saveChunkMeta(): Saving metadata of "
5214 <<count<<" chunks"<<std::endl;
5216 createDirs(m_savedir);
5218 std::string fullpath = m_savedir + "/chunk_meta";
5219 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
5220 if(os.good() == false)
5222 dstream<<"ERROR: ServerMap::saveChunkMeta(): "
5223 <<"could not open"<<fullpath<<std::endl;
5224 throw FileNotGoodException("Cannot open chunk metadata");
5230 os.write((char*)&version, 1);
5235 writeU32(buf, count);
5236 os.write((char*)buf, 4);
5238 for(core::map<v2s16, MapChunk*>::Iterator
5239 i = m_chunks.getIterator();
5240 i.atEnd()==false; i++)
5242 v2s16 p = i.getNode()->getKey();
5243 MapChunk *chunk = i.getNode()->getValue();
5246 os.write((char*)buf, 4);
5248 chunk->serialize(os, version);
5251 setChunksNonModified();
5254 void ServerMap::loadChunkMeta()
5256 DSTACK(__FUNCTION_NAME);
5258 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading chunk metadata"
5261 std::string fullpath = m_savedir + "/chunk_meta";
5262 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5263 if(is.good() == false)
5265 dstream<<"ERROR: ServerMap::loadChunkMeta(): "
5266 <<"could not open"<<fullpath<<std::endl;
5267 throw FileNotGoodException("Cannot open chunk metadata");
5273 is.read((char*)&version, 1);
5278 is.read((char*)buf, 4);
5279 u32 count = readU32(buf);
5281 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading metadata of "
5282 <<count<<" chunks"<<std::endl;
5284 for(u32 i=0; i<count; i++)
5287 MapChunk *chunk = new MapChunk();
5289 is.read((char*)buf, 4);
5292 chunk->deSerialize(is, version);
5293 m_chunks.insert(p, chunk);
5297 void ServerMap::saveSectorMeta(ServerMapSector *sector)
5299 DSTACK(__FUNCTION_NAME);
5300 // Format used for writing
5301 u8 version = SER_FMT_VER_HIGHEST;
5303 v2s16 pos = sector->getPos();
5304 std::string dir = getSectorDir(pos);
5307 std::string fullpath = dir + "/meta";
5308 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5309 if(o.good() == false)
5310 throw FileNotGoodException("Cannot open sector metafile");
5312 sector->serialize(o, version);
5314 sector->differs_from_disk = false;
5317 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
5319 DSTACK(__FUNCTION_NAME);
5321 v2s16 p2d = getSectorPos(sectordir);
5323 ServerMapSector *sector = NULL;
5325 std::string fullpath = sectordir + "/meta";
5326 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5327 if(is.good() == false)
5329 // If the directory exists anyway, it probably is in some old
5330 // format. Just go ahead and create the sector.
5331 if(fs::PathExists(sectordir))
5333 dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
5334 <<fullpath<<" doesn't exist but directory does."
5335 <<" Continuing with a sector with no metadata."
5337 sector = new ServerMapSector(this, p2d);
5338 m_sectors.insert(p2d, sector);
5342 throw FileNotGoodException("Cannot open sector metafile");
5347 sector = ServerMapSector::deSerialize
5348 (is, this, p2d, m_sectors);
5350 saveSectorMeta(sector);
5353 sector->differs_from_disk = false;
5358 bool ServerMap::loadSectorFull(v2s16 p2d)
5360 DSTACK(__FUNCTION_NAME);
5362 MapSector *sector = NULL;
5364 // The directory layout we're going to load from.
5365 // 1 - original sectors/xxxxzzzz/
5366 // 2 - new sectors2/xxx/zzz/
5367 // If we load from anything but the latest structure, we will
5368 // immediately save to the new one, and remove the old.
5370 std::string sectordir1 = getSectorDir(p2d, 1);
5371 std::string sectordir;
5372 if(fs::PathExists(sectordir1))
5374 sectordir = sectordir1;
5379 sectordir = getSectorDir(p2d, 2);
5382 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5385 sector = loadSectorMeta(sectordir, loadlayout != 2);
5387 catch(InvalidFilenameException &e)
5391 catch(FileNotGoodException &e)
5395 catch(std::exception &e)
5403 std::vector<fs::DirListNode> list2 = fs::GetDirListing
5405 std::vector<fs::DirListNode>::iterator i2;
5406 for(i2=list2.begin(); i2!=list2.end(); i2++)
5412 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
5414 catch(InvalidFilenameException &e)
5416 // This catches unknown crap in directory
5422 dstream<<"Sector converted to new layout - deleting "<<
5423 sectordir1<<std::endl;
5424 fs::RecursiveDelete(sectordir1);
5431 void ServerMap::saveBlock(MapBlock *block)
5433 DSTACK(__FUNCTION_NAME);
5435 Dummy blocks are not written
5437 if(block->isDummy())
5439 /*v3s16 p = block->getPos();
5440 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
5441 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
5445 // Format used for writing
5446 u8 version = SER_FMT_VER_HIGHEST;
5448 v3s16 p3d = block->getPos();
5449 v2s16 p2d(p3d.X, p3d.Z);
5450 std::string dir = getSectorDir(p2d);
5454 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
5455 std::string fullpath = dir + "/" + cc;
5456 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5457 if(o.good() == false)
5458 throw FileNotGoodException("Cannot open block data");
5461 [0] u8 serialization version
5464 o.write((char*)&version, 1);
5467 block->serialize(o, version);
5469 // Write extra data stored on disk
5470 block->serializeDiskExtra(o, version);
5472 // We just wrote it to the disk so clear modified flag
5473 block->resetChangedFlag();
5476 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
5478 DSTACK(__FUNCTION_NAME);
5480 std::string fullpath = sectordir+"/"+blockfile;
5483 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5484 if(is.good() == false)
5485 throw FileNotGoodException("Cannot open block file");
5487 v3s16 p3d = getBlockPos(sectordir, blockfile);
5488 v2s16 p2d(p3d.X, p3d.Z);
5490 assert(sector->getPos() == p2d);
5492 u8 version = SER_FMT_VER_INVALID;
5493 is.read((char*)&version, 1);
5496 throw SerializationError("ServerMap::loadBlock(): Failed"
5497 " to read MapBlock version");
5499 /*u32 block_size = MapBlock::serializedLength(version);
5500 SharedBuffer<u8> data(block_size);
5501 is.read((char*)*data, block_size);*/
5503 // This will always return a sector because we're the server
5504 //MapSector *sector = emergeSector(p2d);
5506 MapBlock *block = NULL;
5507 bool created_new = false;
5509 block = sector->getBlockNoCreate(p3d.Y);
5511 catch(InvalidPositionException &e)
5513 block = sector->createBlankBlockNoInsert(p3d.Y);
5518 block->deSerialize(is, version);
5520 // Read extra data stored on disk
5521 block->deSerializeDiskExtra(is, version);
5523 // If it's a new block, insert it to the map
5525 sector->insertBlock(block);
5528 Save blocks loaded in old format in new format
5531 if(version < SER_FMT_VER_HIGHEST || save_after_load)
5536 // We just loaded it from the disk, so it's up-to-date.
5537 block->resetChangedFlag();
5540 catch(SerializationError &e)
5542 dstream<<"WARNING: Invalid block data on disk "
5543 "(SerializationError). "
5546 //" Ignoring. A new one will be generated.
5549 // TODO: Backup file; name is in fullpath.
5553 void ServerMap::PrintInfo(std::ostream &out)
5564 ClientMap::ClientMap(
5566 MapDrawControl &control,
5567 scene::ISceneNode* parent,
5568 scene::ISceneManager* mgr,
5572 scene::ISceneNode(parent, mgr, id),
5575 m_camera_position(0,0,0),
5576 m_camera_direction(0,0,1)
5578 m_camera_mutex.Init();
5579 assert(m_camera_mutex.IsInitialized());
5581 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
5582 BS*1000000,BS*1000000,BS*1000000);
5585 ClientMap::~ClientMap()
5587 /*JMutexAutoLock lock(mesh_mutex);
5596 MapSector * ClientMap::emergeSector(v2s16 p2d)
5598 DSTACK(__FUNCTION_NAME);
5599 // Check that it doesn't exist already
5601 return getSectorNoGenerate(p2d);
5603 catch(InvalidPositionException &e)
5608 ClientMapSector *sector = new ClientMapSector(this, p2d);
5611 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5612 m_sectors.insert(p2d, sector);
5618 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
5620 DSTACK(__FUNCTION_NAME);
5621 ClientMapSector *sector = NULL;
5623 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5625 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
5629 sector = (ClientMapSector*)n->getValue();
5630 assert(sector->getId() == MAPSECTOR_CLIENT);
5634 sector = new ClientMapSector(this, p2d);
5636 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5637 m_sectors.insert(p2d, sector);
5641 sector->deSerialize(is);
5644 void ClientMap::OnRegisterSceneNode()
5648 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5649 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5652 ISceneNode::OnRegisterSceneNode();
5655 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5657 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5658 DSTACK(__FUNCTION_NAME);
5660 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5663 Get time for measuring timeout.
5665 Measuring time is very useful for long delays when the
5666 machine is swapping a lot.
5668 int time1 = time(0);
5670 //u32 daynight_ratio = m_client->getDayNightRatio();
5672 m_camera_mutex.Lock();
5673 v3f camera_position = m_camera_position;
5674 v3f camera_direction = m_camera_direction;
5675 m_camera_mutex.Unlock();
5678 Get all blocks and draw all visible ones
5681 v3s16 cam_pos_nodes(
5682 camera_position.X / BS,
5683 camera_position.Y / BS,
5684 camera_position.Z / BS);
5686 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5688 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5689 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5691 // Take a fair amount as we will be dropping more out later
5693 p_nodes_min.X / MAP_BLOCKSIZE - 1,
5694 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
5695 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
5697 p_nodes_max.X / MAP_BLOCKSIZE,
5698 p_nodes_max.Y / MAP_BLOCKSIZE,
5699 p_nodes_max.Z / MAP_BLOCKSIZE);
5701 u32 vertex_count = 0;
5703 // For limiting number of mesh updates per frame
5704 u32 mesh_update_count = 0;
5706 u32 blocks_would_have_drawn = 0;
5707 u32 blocks_drawn = 0;
5709 //NOTE: The sectors map should be locked but we're not doing it
5710 // because it'd cause too much delays
5712 int timecheck_counter = 0;
5713 core::map<v2s16, MapSector*>::Iterator si;
5714 si = m_sectors.getIterator();
5715 for(; si.atEnd() == false; si++)
5718 timecheck_counter++;
5719 if(timecheck_counter > 50)
5721 timecheck_counter = 0;
5722 int time2 = time(0);
5723 if(time2 > time1 + 4)
5725 dstream<<"ClientMap::renderMap(): "
5726 "Rendering takes ages, returning."
5733 MapSector *sector = si.getNode()->getValue();
5734 v2s16 sp = sector->getPos();
5736 if(m_control.range_all == false)
5738 if(sp.X < p_blocks_min.X
5739 || sp.X > p_blocks_max.X
5740 || sp.Y < p_blocks_min.Z
5741 || sp.Y > p_blocks_max.Z)
5745 core::list< MapBlock * > sectorblocks;
5746 sector->getBlocks(sectorblocks);
5752 core::list< MapBlock * >::Iterator i;
5753 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5755 MapBlock *block = *i;
5758 Compare block position to camera position, skip
5759 if not seen on display
5762 float range = 100000 * BS;
5763 if(m_control.range_all == false)
5764 range = m_control.wanted_range * BS;
5767 if(isBlockInSight(block->getPos(), camera_position,
5768 camera_direction, range, &d) == false)
5773 // This is ugly (spherical distance limit?)
5774 /*if(m_control.range_all == false &&
5775 d - 0.5*BS*MAP_BLOCKSIZE > range)
5780 Update expired mesh (used for day/night change)
5782 It doesn't work exactly like it should now with the
5783 tasked mesh update but whatever.
5786 bool mesh_expired = false;
5789 JMutexAutoLock lock(block->mesh_mutex);
5791 mesh_expired = block->getMeshExpired();
5793 // Mesh has not been expired and there is no mesh:
5794 // block has no content
5795 if(block->mesh == NULL && mesh_expired == false)
5799 f32 faraway = BS*50;
5800 //f32 faraway = m_control.wanted_range * BS;
5803 This has to be done with the mesh_mutex unlocked
5805 // Pretty random but this should work somewhat nicely
5806 if(mesh_expired && (
5807 (mesh_update_count < 3
5808 && (d < faraway || mesh_update_count < 2)
5811 (m_control.range_all && mesh_update_count < 20)
5814 /*if(mesh_expired && mesh_update_count < 6
5815 && (d < faraway || mesh_update_count < 3))*/
5817 mesh_update_count++;
5819 // Mesh has been expired: generate new mesh
5820 //block->updateMesh(daynight_ratio);
5821 m_client->addUpdateMeshTask(block->getPos());
5823 mesh_expired = false;
5828 Draw the faces of the block
5831 JMutexAutoLock lock(block->mesh_mutex);
5833 scene::SMesh *mesh = block->mesh;
5838 blocks_would_have_drawn++;
5839 if(blocks_drawn >= m_control.wanted_max_blocks
5840 && m_control.range_all == false
5841 && d > m_control.wanted_min_range * BS)
5845 u32 c = mesh->getMeshBufferCount();
5847 for(u32 i=0; i<c; i++)
5849 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
5850 const video::SMaterial& material = buf->getMaterial();
5851 video::IMaterialRenderer* rnd =
5852 driver->getMaterialRenderer(material.MaterialType);
5853 bool transparent = (rnd && rnd->isTransparent());
5854 // Render transparent on transparent pass and likewise.
5855 if(transparent == is_transparent_pass)
5858 This *shouldn't* hurt too much because Irrlicht
5859 doesn't change opengl textures if the old
5860 material is set again.
5862 driver->setMaterial(buf->getMaterial());
5863 driver->drawMeshBuffer(buf);
5864 vertex_count += buf->getVertexCount();
5868 } // foreach sectorblocks
5871 m_control.blocks_drawn = blocks_drawn;
5872 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
5874 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
5875 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
5878 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
5879 core::map<v3s16, MapBlock*> *affected_blocks)
5881 bool changed = false;
5883 Add it to all blocks touching it
5886 v3s16(0,0,0), // this
5887 v3s16(0,0,1), // back
5888 v3s16(0,1,0), // top
5889 v3s16(1,0,0), // right
5890 v3s16(0,0,-1), // front
5891 v3s16(0,-1,0), // bottom
5892 v3s16(-1,0,0), // left
5894 for(u16 i=0; i<7; i++)
5896 v3s16 p2 = p + dirs[i];
5897 // Block position of neighbor (or requested) node
5898 v3s16 blockpos = getNodeBlockPos(p2);
5899 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5900 if(blockref == NULL)
5902 // Relative position of requested node
5903 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5904 if(blockref->setTempMod(relpos, mod))
5909 if(changed && affected_blocks!=NULL)
5911 for(u16 i=0; i<7; i++)
5913 v3s16 p2 = p + dirs[i];
5914 // Block position of neighbor (or requested) node
5915 v3s16 blockpos = getNodeBlockPos(p2);
5916 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5917 if(blockref == NULL)
5919 affected_blocks->insert(blockpos, blockref);
5925 bool ClientMap::clearTempMod(v3s16 p,
5926 core::map<v3s16, MapBlock*> *affected_blocks)
5928 bool changed = false;
5930 v3s16(0,0,0), // this
5931 v3s16(0,0,1), // back
5932 v3s16(0,1,0), // top
5933 v3s16(1,0,0), // right
5934 v3s16(0,0,-1), // front
5935 v3s16(0,-1,0), // bottom
5936 v3s16(-1,0,0), // left
5938 for(u16 i=0; i<7; i++)
5940 v3s16 p2 = p + dirs[i];
5941 // Block position of neighbor (or requested) node
5942 v3s16 blockpos = getNodeBlockPos(p2);
5943 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5944 if(blockref == NULL)
5946 // Relative position of requested node
5947 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5948 if(blockref->clearTempMod(relpos))
5953 if(changed && affected_blocks!=NULL)
5955 for(u16 i=0; i<7; i++)
5957 v3s16 p2 = p + dirs[i];
5958 // Block position of neighbor (or requested) node
5959 v3s16 blockpos = getNodeBlockPos(p2);
5960 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5961 if(blockref == NULL)
5963 affected_blocks->insert(blockpos, blockref);
5969 void ClientMap::expireMeshes(bool only_daynight_diffed)
5971 TimeTaker timer("expireMeshes()");
5973 core::map<v2s16, MapSector*>::Iterator si;
5974 si = m_sectors.getIterator();
5975 for(; si.atEnd() == false; si++)
5977 MapSector *sector = si.getNode()->getValue();
5979 core::list< MapBlock * > sectorblocks;
5980 sector->getBlocks(sectorblocks);
5982 core::list< MapBlock * >::Iterator i;
5983 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5985 MapBlock *block = *i;
5987 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
5993 JMutexAutoLock lock(block->mesh_mutex);
5994 if(block->mesh != NULL)
5996 /*block->mesh->drop();
5997 block->mesh = NULL;*/
5998 block->setMeshExpired(true);
6005 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
6007 assert(mapType() == MAPTYPE_CLIENT);
6010 v3s16 p = blockpos + v3s16(0,0,0);
6011 MapBlock *b = getBlockNoCreate(p);
6012 b->updateMesh(daynight_ratio);
6013 //b->setMeshExpired(true);
6015 catch(InvalidPositionException &e){}
6018 v3s16 p = blockpos + v3s16(-1,0,0);
6019 MapBlock *b = getBlockNoCreate(p);
6020 b->updateMesh(daynight_ratio);
6021 //b->setMeshExpired(true);
6023 catch(InvalidPositionException &e){}
6025 v3s16 p = blockpos + v3s16(0,-1,0);
6026 MapBlock *b = getBlockNoCreate(p);
6027 b->updateMesh(daynight_ratio);
6028 //b->setMeshExpired(true);
6030 catch(InvalidPositionException &e){}
6032 v3s16 p = blockpos + v3s16(0,0,-1);
6033 MapBlock *b = getBlockNoCreate(p);
6034 b->updateMesh(daynight_ratio);
6035 //b->setMeshExpired(true);
6037 catch(InvalidPositionException &e){}
6042 Update mesh of block in which the node is, and if the node is at the
6043 leading edge, update the appropriate leading blocks too.
6045 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
6053 v3s16 blockposes[4];
6054 for(u32 i=0; i<4; i++)
6056 v3s16 np = nodepos + dirs[i];
6057 blockposes[i] = getNodeBlockPos(np);
6058 // Don't update mesh of block if it has been done already
6059 bool already_updated = false;
6060 for(u32 j=0; j<i; j++)
6062 if(blockposes[j] == blockposes[i])
6064 already_updated = true;
6071 MapBlock *b = getBlockNoCreate(blockposes[i]);
6072 b->updateMesh(daynight_ratio);
6077 void ClientMap::PrintInfo(std::ostream &out)
6088 MapVoxelManipulator::MapVoxelManipulator(Map *map)
6093 MapVoxelManipulator::~MapVoxelManipulator()
6095 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
6099 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
6101 TimeTaker timer1("emerge", &emerge_time);
6103 // Units of these are MapBlocks
6104 v3s16 p_min = getNodeBlockPos(a.MinEdge);
6105 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
6107 VoxelArea block_area_nodes
6108 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6110 addArea(block_area_nodes);
6112 for(s32 z=p_min.Z; z<=p_max.Z; z++)
6113 for(s32 y=p_min.Y; y<=p_max.Y; y++)
6114 for(s32 x=p_min.X; x<=p_max.X; x++)
6117 core::map<v3s16, bool>::Node *n;
6118 n = m_loaded_blocks.find(p);
6122 bool block_data_inexistent = false;
6125 TimeTaker timer1("emerge load", &emerge_load_time);
6127 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
6128 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
6131 dstream<<std::endl;*/
6133 MapBlock *block = m_map->getBlockNoCreate(p);
6134 if(block->isDummy())
6135 block_data_inexistent = true;
6137 block->copyTo(*this);
6139 catch(InvalidPositionException &e)
6141 block_data_inexistent = true;
6144 if(block_data_inexistent)
6146 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6147 // Fill with VOXELFLAG_INEXISTENT
6148 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
6149 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
6151 s32 i = m_area.index(a.MinEdge.X,y,z);
6152 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
6156 m_loaded_blocks.insert(p, !block_data_inexistent);
6159 //dstream<<"emerge done"<<std::endl;
6163 SUGG: Add an option to only update eg. water and air nodes.
6164 This will make it interfere less with important stuff if
6167 void MapVoxelManipulator::blitBack
6168 (core::map<v3s16, MapBlock*> & modified_blocks)
6170 if(m_area.getExtent() == v3s16(0,0,0))
6173 //TimeTaker timer1("blitBack");
6175 /*dstream<<"blitBack(): m_loaded_blocks.size()="
6176 <<m_loaded_blocks.size()<<std::endl;*/
6179 Initialize block cache
6181 v3s16 blockpos_last;
6182 MapBlock *block = NULL;
6183 bool block_checked_in_modified = false;
6185 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
6186 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
6187 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
6191 u8 f = m_flags[m_area.index(p)];
6192 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
6195 MapNode &n = m_data[m_area.index(p)];
6197 v3s16 blockpos = getNodeBlockPos(p);
6202 if(block == NULL || blockpos != blockpos_last){
6203 block = m_map->getBlockNoCreate(blockpos);
6204 blockpos_last = blockpos;
6205 block_checked_in_modified = false;
6208 // Calculate relative position in block
6209 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
6211 // Don't continue if nothing has changed here
6212 if(block->getNode(relpos) == n)
6215 //m_map->setNode(m_area.MinEdge + p, n);
6216 block->setNode(relpos, n);
6219 Make sure block is in modified_blocks
6221 if(block_checked_in_modified == false)
6223 modified_blocks[blockpos] = block;
6224 block_checked_in_modified = true;
6227 catch(InvalidPositionException &e)
6233 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
6234 MapVoxelManipulator(map),
6235 m_create_area(false)
6239 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
6243 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
6245 // Just create the area so that it can be pointed to
6246 VoxelManipulator::emerge(a, caller_id);
6249 void ManualMapVoxelManipulator::initialEmerge(
6250 v3s16 blockpos_min, v3s16 blockpos_max)
6252 TimeTaker timer1("initialEmerge", &emerge_time);
6254 // Units of these are MapBlocks
6255 v3s16 p_min = blockpos_min;
6256 v3s16 p_max = blockpos_max;
6258 VoxelArea block_area_nodes
6259 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6261 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
6264 dstream<<"initialEmerge: area: ";
6265 block_area_nodes.print(dstream);
6266 dstream<<" ("<<size_MB<<"MB)";
6270 addArea(block_area_nodes);
6272 for(s32 z=p_min.Z; z<=p_max.Z; z++)
6273 for(s32 y=p_min.Y; y<=p_max.Y; y++)
6274 for(s32 x=p_min.X; x<=p_max.X; x++)
6277 core::map<v3s16, bool>::Node *n;
6278 n = m_loaded_blocks.find(p);
6282 bool block_data_inexistent = false;
6285 TimeTaker timer1("emerge load", &emerge_load_time);
6287 MapBlock *block = m_map->getBlockNoCreate(p);
6288 if(block->isDummy())
6289 block_data_inexistent = true;
6291 block->copyTo(*this);
6293 catch(InvalidPositionException &e)
6295 block_data_inexistent = true;
6298 if(block_data_inexistent)
6301 Mark area inexistent
6303 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6304 // Fill with VOXELFLAG_INEXISTENT
6305 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
6306 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
6308 s32 i = m_area.index(a.MinEdge.X,y,z);
6309 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
6313 m_loaded_blocks.insert(p, !block_data_inexistent);
6317 void ManualMapVoxelManipulator::blitBackAll(
6318 core::map<v3s16, MapBlock*> * modified_blocks)
6320 if(m_area.getExtent() == v3s16(0,0,0))
6324 Copy data of all blocks
6326 for(core::map<v3s16, bool>::Iterator
6327 i = m_loaded_blocks.getIterator();
6328 i.atEnd() == false; i++)
6330 bool existed = i.getNode()->getValue();
6331 if(existed == false)
6333 v3s16 p = i.getNode()->getKey();
6334 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
6337 dstream<<"WARNING: "<<__FUNCTION_NAME
6338 <<": got NULL block "
6339 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
6344 block->copyFrom(*this);
6347 modified_blocks->insert(p, block);