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
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 //TODO: change to iron_amount or whatever
4549 u16 iron_amount = 15;
4550 u16 iron_rareness = 60 / iron_amount;
4551 if(iron_rareness == 0)
4553 if(myrand()%iron_rareness == 0)
4555 u16 a = myrand() % 16;
4556 u16 amount = iron_amount * a*a*a / 1000;
4557 for(s16 i=0; i<amount; i++)
4560 (myrand()%(MAP_BLOCKSIZE-2))+1,
4561 (myrand()%(MAP_BLOCKSIZE-2))+1,
4562 (myrand()%(MAP_BLOCKSIZE-2))+1
4566 n.d = CONTENT_STONE;
4567 n.param = MINERAL_IRON;
4569 for(u16 i=0; i<27; i++)
4571 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4573 block->setNode(cp+g_27dirs[i], n);
4580 Create a few rats in empty blocks underground
4582 if(completely_underground)
4584 //for(u16 i=0; i<2; i++)
4587 (myrand()%(MAP_BLOCKSIZE-2))+1,
4588 (myrand()%(MAP_BLOCKSIZE-2))+1,
4589 (myrand()%(MAP_BLOCKSIZE-2))+1
4592 // Check that the place is empty
4593 //if(!is_ground_content(block->getNode(cp).d))
4596 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp, BS));
4597 block->addObject(obj);
4602 #endif // end of proper block generation
4605 Add block to sector.
4607 sector->insertBlock(block);
4609 // Lighting is invalid after generation.
4610 block->setLightingExpired(true);
4617 <<"lighting_invalidated_blocks.size()"
4621 <<" "<<lighting_invalidated_blocks.size()
4623 <<", "<<completely_underground
4624 <<", "<<some_part_underground
4631 MapBlock * ServerMap::createBlock(v3s16 p)
4633 DSTACKF("%s: p=(%d,%d,%d)",
4634 __FUNCTION_NAME, p.X, p.Y, p.Z);
4637 Do not create over-limit
4639 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4640 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4641 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4642 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4643 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4644 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4645 throw InvalidPositionException("createBlock(): pos. over limit");
4647 v2s16 p2d(p.X, p.Z);
4650 This will create or load a sector if not found in memory.
4651 If block exists on disk, it will be loaded.
4653 NOTE: On old save formats, this will be slow, as it generates
4654 lighting on blocks for them.
4656 ServerMapSector *sector;
4658 sector = (ServerMapSector*)createSector(p2d);
4659 assert(sector->getId() == MAPSECTOR_SERVER);
4661 catch(InvalidPositionException &e)
4663 dstream<<"createBlock: createSector() failed"<<std::endl;
4667 NOTE: This should not be done, or at least the exception
4668 should not be passed on as std::exception, because it
4669 won't be catched at all.
4671 /*catch(std::exception &e)
4673 dstream<<"createBlock: createSector() failed: "
4674 <<e.what()<<std::endl;
4679 Try to get a block from the sector
4682 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4686 block = sector->createBlankBlock(block_y);
4690 MapBlock * ServerMap::emergeBlock(
4692 bool only_from_disk,
4693 core::map<v3s16, MapBlock*> &changed_blocks,
4694 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4697 DSTACKF("%s: p=(%d,%d,%d), only_from_disk=%d",
4699 p.X, p.Y, p.Z, only_from_disk);
4702 Do not generate over-limit
4704 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4705 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4706 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4707 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4708 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4709 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4710 throw InvalidPositionException("emergeBlock(): pos. over limit");
4712 v2s16 p2d(p.X, p.Z);
4715 This will create or load a sector if not found in memory.
4716 If block exists on disk, it will be loaded.
4718 ServerMapSector *sector;
4720 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
4721 assert(sector->getId() == MAPSECTOR_SERVER);
4723 catch(InvalidPositionException &e)
4725 dstream<<"emergeBlock: emergeSector() failed: "
4726 <<e.what()<<std::endl;
4727 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4729 <<"You could try to delete it."<<std::endl;
4732 catch(VersionMismatchException &e)
4734 dstream<<"emergeBlock: emergeSector() failed: "
4735 <<e.what()<<std::endl;
4736 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4738 <<"You could try to delete it."<<std::endl;
4742 NOTE: This should not be done, or at least the exception
4743 should not be passed on as std::exception, because it
4744 won't be catched at all.
4746 /*catch(std::exception &e)
4748 dstream<<"emergeBlock: emergeSector() failed: "
4749 <<e.what()<<std::endl;
4750 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4752 <<"You could try to delete it."<<std::endl;
4757 Try to get a block from the sector
4760 bool does_not_exist = false;
4761 bool lighting_expired = false;
4762 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4766 does_not_exist = true;
4768 else if(block->isDummy() == true)
4770 does_not_exist = true;
4772 else if(block->getLightingExpired())
4774 lighting_expired = true;
4779 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
4784 If block was not found on disk and not going to generate a
4785 new one, make sure there is a dummy block in place.
4787 if(only_from_disk && (does_not_exist || lighting_expired))
4789 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
4793 // Create dummy block
4794 block = new MapBlock(this, p, true);
4796 // Add block to sector
4797 sector->insertBlock(block);
4803 //dstream<<"Not found on disk, generating."<<std::endl;
4805 //TimeTaker("emergeBlock() generate");
4807 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
4810 If the block doesn't exist, generate the block.
4814 block = generateBlock(p, block, sector, changed_blocks,
4815 lighting_invalidated_blocks);
4818 if(lighting_expired)
4820 lighting_invalidated_blocks.insert(p, block);
4824 Initially update sunlight
4828 core::map<v3s16, bool> light_sources;
4829 bool black_air_left = false;
4830 bool bottom_invalid =
4831 block->propagateSunlight(light_sources, true,
4834 // If sunlight didn't reach everywhere and part of block is
4835 // above ground, lighting has to be properly updated
4836 //if(black_air_left && some_part_underground)
4839 lighting_invalidated_blocks[block->getPos()] = block;
4844 lighting_invalidated_blocks[block->getPos()] = block;
4851 s16 ServerMap::findGroundLevel(v2s16 p2d)
4854 Uh, just do something random...
4856 // Find existing map from top to down
4859 v3s16 p(p2d.X, max, p2d.Y);
4860 for(; p.Y>min; p.Y--)
4862 MapNode n = getNodeNoEx(p);
4863 if(n.d != CONTENT_IGNORE)
4868 // If this node is not air, go to plan b
4869 if(getNodeNoEx(p).d != CONTENT_AIR)
4871 // Search existing walkable and return it
4872 for(; p.Y>min; p.Y--)
4874 MapNode n = getNodeNoEx(p);
4875 if(content_walkable(n.d) && n.d != CONTENT_IGNORE)
4881 Plan B: Get from map generator perlin noise function
4883 // This won't work if proper generation is disabled
4884 if(m_chunksize == 0)
4885 return WATER_LEVEL+2;
4886 double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
4890 void ServerMap::createDirs(std::string path)
4892 if(fs::CreateAllDirs(path) == false)
4894 m_dout<<DTIME<<"ServerMap: Failed to create directory "
4895 <<"\""<<path<<"\""<<std::endl;
4896 throw BaseException("ServerMap failed to create directory");
4900 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
4906 snprintf(cc, 9, "%.4x%.4x",
4907 (unsigned int)pos.X&0xffff,
4908 (unsigned int)pos.Y&0xffff);
4910 return m_savedir + "/sectors/" + cc;
4912 snprintf(cc, 9, "%.3x/%.3x",
4913 (unsigned int)pos.X&0xfff,
4914 (unsigned int)pos.Y&0xfff);
4916 return m_savedir + "/sectors2/" + cc;
4922 v2s16 ServerMap::getSectorPos(std::string dirname)
4926 size_t spos = dirname.rfind('/') + 1;
4927 assert(spos != std::string::npos);
4928 if(dirname.size() - spos == 8)
4931 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
4933 else if(dirname.size() - spos == 3)
4936 r = sscanf(dirname.substr(spos-4).c_str(), "%3x/%3x", &x, &y);
4937 // Sign-extend the 12 bit values up to 16 bits...
4938 if(x&0x800) x|=0xF000;
4939 if(y&0x800) y|=0xF000;
4946 v2s16 pos((s16)x, (s16)y);
4950 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
4952 v2s16 p2d = getSectorPos(sectordir);
4954 if(blockfile.size() != 4){
4955 throw InvalidFilenameException("Invalid block filename");
4958 int r = sscanf(blockfile.c_str(), "%4x", &y);
4960 throw InvalidFilenameException("Invalid block filename");
4961 return v3s16(p2d.X, y, p2d.Y);
4964 void ServerMap::save(bool only_changed)
4966 DSTACK(__FUNCTION_NAME);
4967 if(m_map_saving_enabled == false)
4969 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
4973 if(only_changed == false)
4974 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
4977 if(only_changed == false || m_map_metadata_changed)
4982 // Disable saving chunk metadata if chunks are disabled
4983 if(m_chunksize != 0)
4985 if(only_changed == false || anyChunkModified())
4989 u32 sector_meta_count = 0;
4990 u32 block_count = 0;
4991 u32 block_count_all = 0; // Number of blocks in memory
4993 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
4994 for(; i.atEnd() == false; i++)
4996 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
4997 assert(sector->getId() == MAPSECTOR_SERVER);
4999 if(sector->differs_from_disk || only_changed == false)
5001 saveSectorMeta(sector);
5002 sector_meta_count++;
5004 core::list<MapBlock*> blocks;
5005 sector->getBlocks(blocks);
5006 core::list<MapBlock*>::Iterator j;
5007 for(j=blocks.begin(); j!=blocks.end(); j++)
5009 MapBlock *block = *j;
5013 if(block->getChangedFlag() || only_changed == false)
5018 /*dstream<<"ServerMap: Written block ("
5019 <<block->getPos().X<<","
5020 <<block->getPos().Y<<","
5021 <<block->getPos().Z<<")"
5028 Only print if something happened or saved whole map
5030 if(only_changed == false || sector_meta_count != 0
5031 || block_count != 0)
5033 dstream<<DTIME<<"ServerMap: Written: "
5034 <<sector_meta_count<<" sector metadata files, "
5035 <<block_count<<" block files"
5036 <<", "<<block_count_all<<" blocks in memory."
5042 // NOTE: Doing this is insane. Deprecated and probably broken.
5043 void ServerMap::loadAll()
5045 DSTACK(__FUNCTION_NAME);
5046 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
5051 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
5053 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
5055 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5058 s32 printed_counter = -100000;
5059 s32 count = list.size();
5061 std::vector<fs::DirListNode>::iterator i;
5062 for(i=list.begin(); i!=list.end(); i++)
5064 if(counter > printed_counter + 10)
5066 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
5067 printed_counter = counter;
5071 MapSector *sector = NULL;
5073 // We want directories
5077 sector = loadSectorMeta(i->name);
5079 catch(InvalidFilenameException &e)
5081 // This catches unknown crap in directory
5084 std::vector<fs::DirListNode> list2 = fs::GetDirListing
5085 (m_savedir+"/sectors/"+i->name);
5086 std::vector<fs::DirListNode>::iterator i2;
5087 for(i2=list2.begin(); i2!=list2.end(); i2++)
5093 loadBlock(i->name, i2->name, sector);
5095 catch(InvalidFilenameException &e)
5097 // This catches unknown crap in directory
5101 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
5106 void ServerMap::saveMasterHeightmap()
5108 DSTACK(__FUNCTION_NAME);
5110 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
5112 createDir(m_savedir);
5114 /*std::string fullpath = m_savedir + "/master_heightmap";
5115 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5116 if(o.good() == false)
5117 throw FileNotGoodException("Cannot open master heightmap");*/
5119 // Format used for writing
5120 //u8 version = SER_FMT_VER_HIGHEST;
5123 void ServerMap::loadMasterHeightmap()
5125 DSTACK(__FUNCTION_NAME);
5127 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
5129 /*std::string fullpath = m_savedir + "/master_heightmap";
5130 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5131 if(is.good() == false)
5132 throw FileNotGoodException("Cannot open master heightmap");*/
5136 void ServerMap::saveMapMeta()
5138 DSTACK(__FUNCTION_NAME);
5140 dstream<<"INFO: ServerMap::saveMapMeta(): "
5141 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
5144 createDirs(m_savedir);
5146 std::string fullpath = m_savedir + "/map_meta.txt";
5147 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
5148 if(os.good() == false)
5150 dstream<<"ERROR: ServerMap::saveMapMeta(): "
5151 <<"could not open"<<fullpath<<std::endl;
5152 throw FileNotGoodException("Cannot open chunk metadata");
5156 params.setU64("seed", m_seed);
5157 params.setS32("chunksize", m_chunksize);
5159 params.writeLines(os);
5161 os<<"[end_of_params]\n";
5163 m_map_metadata_changed = false;
5166 void ServerMap::loadMapMeta()
5168 DSTACK(__FUNCTION_NAME);
5170 dstream<<"INFO: ServerMap::loadMapMeta(): Loading map metadata"
5173 std::string fullpath = m_savedir + "/map_meta.txt";
5174 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5175 if(is.good() == false)
5177 dstream<<"ERROR: ServerMap::loadMapMeta(): "
5178 <<"could not open"<<fullpath<<std::endl;
5179 throw FileNotGoodException("Cannot open map metadata");
5187 throw SerializationError
5188 ("ServerMap::loadMapMeta(): [end_of_params] not found");
5190 std::getline(is, line);
5191 std::string trimmedline = trim(line);
5192 if(trimmedline == "[end_of_params]")
5194 params.parseConfigLine(line);
5197 m_seed = params.getU64("seed");
5198 m_chunksize = params.getS32("chunksize");
5200 dstream<<"INFO: ServerMap::loadMapMeta(): "
5201 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
5205 void ServerMap::saveChunkMeta()
5207 DSTACK(__FUNCTION_NAME);
5209 // This should not be called if chunks are disabled.
5210 assert(m_chunksize != 0);
5212 u32 count = m_chunks.size();
5214 dstream<<"INFO: ServerMap::saveChunkMeta(): Saving metadata of "
5215 <<count<<" chunks"<<std::endl;
5217 createDirs(m_savedir);
5219 std::string fullpath = m_savedir + "/chunk_meta";
5220 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
5221 if(os.good() == false)
5223 dstream<<"ERROR: ServerMap::saveChunkMeta(): "
5224 <<"could not open"<<fullpath<<std::endl;
5225 throw FileNotGoodException("Cannot open chunk metadata");
5231 os.write((char*)&version, 1);
5236 writeU32(buf, count);
5237 os.write((char*)buf, 4);
5239 for(core::map<v2s16, MapChunk*>::Iterator
5240 i = m_chunks.getIterator();
5241 i.atEnd()==false; i++)
5243 v2s16 p = i.getNode()->getKey();
5244 MapChunk *chunk = i.getNode()->getValue();
5247 os.write((char*)buf, 4);
5249 chunk->serialize(os, version);
5252 setChunksNonModified();
5255 void ServerMap::loadChunkMeta()
5257 DSTACK(__FUNCTION_NAME);
5259 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading chunk metadata"
5262 std::string fullpath = m_savedir + "/chunk_meta";
5263 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5264 if(is.good() == false)
5266 dstream<<"ERROR: ServerMap::loadChunkMeta(): "
5267 <<"could not open"<<fullpath<<std::endl;
5268 throw FileNotGoodException("Cannot open chunk metadata");
5274 is.read((char*)&version, 1);
5279 is.read((char*)buf, 4);
5280 u32 count = readU32(buf);
5282 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading metadata of "
5283 <<count<<" chunks"<<std::endl;
5285 for(u32 i=0; i<count; i++)
5288 MapChunk *chunk = new MapChunk();
5290 is.read((char*)buf, 4);
5293 chunk->deSerialize(is, version);
5294 m_chunks.insert(p, chunk);
5298 void ServerMap::saveSectorMeta(ServerMapSector *sector)
5300 DSTACK(__FUNCTION_NAME);
5301 // Format used for writing
5302 u8 version = SER_FMT_VER_HIGHEST;
5304 v2s16 pos = sector->getPos();
5305 std::string dir = getSectorDir(pos);
5308 std::string fullpath = dir + "/meta";
5309 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5310 if(o.good() == false)
5311 throw FileNotGoodException("Cannot open sector metafile");
5313 sector->serialize(o, version);
5315 sector->differs_from_disk = false;
5318 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
5320 DSTACK(__FUNCTION_NAME);
5322 v2s16 p2d = getSectorPos(sectordir);
5324 ServerMapSector *sector = NULL;
5326 std::string fullpath = sectordir + "/meta";
5327 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5328 if(is.good() == false)
5330 // If the directory exists anyway, it probably is in some old
5331 // format. Just go ahead and create the sector.
5332 if(fs::PathExists(sectordir))
5334 dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
5335 <<fullpath<<" doesn't exist but directory does."
5336 <<" Continuing with a sector with no metadata."
5338 sector = new ServerMapSector(this, p2d);
5339 m_sectors.insert(p2d, sector);
5343 throw FileNotGoodException("Cannot open sector metafile");
5348 sector = ServerMapSector::deSerialize
5349 (is, this, p2d, m_sectors);
5351 saveSectorMeta(sector);
5354 sector->differs_from_disk = false;
5359 bool ServerMap::loadSectorFull(v2s16 p2d)
5361 DSTACK(__FUNCTION_NAME);
5363 MapSector *sector = NULL;
5365 // The directory layout we're going to load from.
5366 // 1 - original sectors/xxxxzzzz/
5367 // 2 - new sectors2/xxx/zzz/
5368 // If we load from anything but the latest structure, we will
5369 // immediately save to the new one, and remove the old.
5371 std::string sectordir1 = getSectorDir(p2d, 1);
5372 std::string sectordir;
5373 if(fs::PathExists(sectordir1))
5375 sectordir = sectordir1;
5380 sectordir = getSectorDir(p2d, 2);
5383 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5386 sector = loadSectorMeta(sectordir, loadlayout != 2);
5388 catch(InvalidFilenameException &e)
5392 catch(FileNotGoodException &e)
5396 catch(std::exception &e)
5404 std::vector<fs::DirListNode> list2 = fs::GetDirListing
5406 std::vector<fs::DirListNode>::iterator i2;
5407 for(i2=list2.begin(); i2!=list2.end(); i2++)
5413 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
5415 catch(InvalidFilenameException &e)
5417 // This catches unknown crap in directory
5423 dstream<<"Sector converted to new layout - deleting "<<
5424 sectordir1<<std::endl;
5425 fs::RecursiveDelete(sectordir1);
5432 void ServerMap::saveBlock(MapBlock *block)
5434 DSTACK(__FUNCTION_NAME);
5436 Dummy blocks are not written
5438 if(block->isDummy())
5440 /*v3s16 p = block->getPos();
5441 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
5442 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
5446 // Format used for writing
5447 u8 version = SER_FMT_VER_HIGHEST;
5449 v3s16 p3d = block->getPos();
5450 v2s16 p2d(p3d.X, p3d.Z);
5451 std::string dir = getSectorDir(p2d);
5455 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
5456 std::string fullpath = dir + "/" + cc;
5457 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5458 if(o.good() == false)
5459 throw FileNotGoodException("Cannot open block data");
5462 [0] u8 serialization version
5465 o.write((char*)&version, 1);
5468 block->serialize(o, version);
5470 // Write extra data stored on disk
5471 block->serializeDiskExtra(o, version);
5473 // We just wrote it to the disk so clear modified flag
5474 block->resetChangedFlag();
5477 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
5479 DSTACK(__FUNCTION_NAME);
5481 std::string fullpath = sectordir+"/"+blockfile;
5484 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5485 if(is.good() == false)
5486 throw FileNotGoodException("Cannot open block file");
5488 v3s16 p3d = getBlockPos(sectordir, blockfile);
5489 v2s16 p2d(p3d.X, p3d.Z);
5491 assert(sector->getPos() == p2d);
5493 u8 version = SER_FMT_VER_INVALID;
5494 is.read((char*)&version, 1);
5497 throw SerializationError("ServerMap::loadBlock(): Failed"
5498 " to read MapBlock version");
5500 /*u32 block_size = MapBlock::serializedLength(version);
5501 SharedBuffer<u8> data(block_size);
5502 is.read((char*)*data, block_size);*/
5504 // This will always return a sector because we're the server
5505 //MapSector *sector = emergeSector(p2d);
5507 MapBlock *block = NULL;
5508 bool created_new = false;
5510 block = sector->getBlockNoCreate(p3d.Y);
5512 catch(InvalidPositionException &e)
5514 block = sector->createBlankBlockNoInsert(p3d.Y);
5519 block->deSerialize(is, version);
5521 // Read extra data stored on disk
5522 block->deSerializeDiskExtra(is, version);
5524 // If it's a new block, insert it to the map
5526 sector->insertBlock(block);
5529 Save blocks loaded in old format in new format
5532 if(version < SER_FMT_VER_HIGHEST || save_after_load)
5537 // We just loaded it from the disk, so it's up-to-date.
5538 block->resetChangedFlag();
5541 catch(SerializationError &e)
5543 dstream<<"WARNING: Invalid block data on disk "
5544 "(SerializationError). "
5547 //" Ignoring. A new one will be generated.
5550 // TODO: Backup file; name is in fullpath.
5554 void ServerMap::PrintInfo(std::ostream &out)
5565 ClientMap::ClientMap(
5567 MapDrawControl &control,
5568 scene::ISceneNode* parent,
5569 scene::ISceneManager* mgr,
5573 scene::ISceneNode(parent, mgr, id),
5576 m_camera_position(0,0,0),
5577 m_camera_direction(0,0,1)
5579 m_camera_mutex.Init();
5580 assert(m_camera_mutex.IsInitialized());
5582 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
5583 BS*1000000,BS*1000000,BS*1000000);
5586 ClientMap::~ClientMap()
5588 /*JMutexAutoLock lock(mesh_mutex);
5597 MapSector * ClientMap::emergeSector(v2s16 p2d)
5599 DSTACK(__FUNCTION_NAME);
5600 // Check that it doesn't exist already
5602 return getSectorNoGenerate(p2d);
5604 catch(InvalidPositionException &e)
5609 ClientMapSector *sector = new ClientMapSector(this, p2d);
5612 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5613 m_sectors.insert(p2d, sector);
5619 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
5621 DSTACK(__FUNCTION_NAME);
5622 ClientMapSector *sector = NULL;
5624 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5626 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
5630 sector = (ClientMapSector*)n->getValue();
5631 assert(sector->getId() == MAPSECTOR_CLIENT);
5635 sector = new ClientMapSector(this, p2d);
5637 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5638 m_sectors.insert(p2d, sector);
5642 sector->deSerialize(is);
5645 void ClientMap::OnRegisterSceneNode()
5649 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5650 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5653 ISceneNode::OnRegisterSceneNode();
5656 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5658 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5659 DSTACK(__FUNCTION_NAME);
5661 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5664 Get time for measuring timeout.
5666 Measuring time is very useful for long delays when the
5667 machine is swapping a lot.
5669 int time1 = time(0);
5671 //u32 daynight_ratio = m_client->getDayNightRatio();
5673 m_camera_mutex.Lock();
5674 v3f camera_position = m_camera_position;
5675 v3f camera_direction = m_camera_direction;
5676 m_camera_mutex.Unlock();
5679 Get all blocks and draw all visible ones
5682 v3s16 cam_pos_nodes(
5683 camera_position.X / BS,
5684 camera_position.Y / BS,
5685 camera_position.Z / BS);
5687 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5689 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5690 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5692 // Take a fair amount as we will be dropping more out later
5694 p_nodes_min.X / MAP_BLOCKSIZE - 1,
5695 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
5696 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
5698 p_nodes_max.X / MAP_BLOCKSIZE,
5699 p_nodes_max.Y / MAP_BLOCKSIZE,
5700 p_nodes_max.Z / MAP_BLOCKSIZE);
5702 u32 vertex_count = 0;
5704 // For limiting number of mesh updates per frame
5705 u32 mesh_update_count = 0;
5707 u32 blocks_would_have_drawn = 0;
5708 u32 blocks_drawn = 0;
5710 //NOTE: The sectors map should be locked but we're not doing it
5711 // because it'd cause too much delays
5713 int timecheck_counter = 0;
5714 core::map<v2s16, MapSector*>::Iterator si;
5715 si = m_sectors.getIterator();
5716 for(; si.atEnd() == false; si++)
5719 timecheck_counter++;
5720 if(timecheck_counter > 50)
5722 timecheck_counter = 0;
5723 int time2 = time(0);
5724 if(time2 > time1 + 4)
5726 dstream<<"ClientMap::renderMap(): "
5727 "Rendering takes ages, returning."
5734 MapSector *sector = si.getNode()->getValue();
5735 v2s16 sp = sector->getPos();
5737 if(m_control.range_all == false)
5739 if(sp.X < p_blocks_min.X
5740 || sp.X > p_blocks_max.X
5741 || sp.Y < p_blocks_min.Z
5742 || sp.Y > p_blocks_max.Z)
5746 core::list< MapBlock * > sectorblocks;
5747 sector->getBlocks(sectorblocks);
5753 core::list< MapBlock * >::Iterator i;
5754 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5756 MapBlock *block = *i;
5759 Compare block position to camera position, skip
5760 if not seen on display
5763 float range = 100000 * BS;
5764 if(m_control.range_all == false)
5765 range = m_control.wanted_range * BS;
5768 if(isBlockInSight(block->getPos(), camera_position,
5769 camera_direction, range, &d) == false)
5774 // This is ugly (spherical distance limit?)
5775 /*if(m_control.range_all == false &&
5776 d - 0.5*BS*MAP_BLOCKSIZE > range)
5781 Update expired mesh (used for day/night change)
5783 It doesn't work exactly like it should now with the
5784 tasked mesh update but whatever.
5787 bool mesh_expired = false;
5790 JMutexAutoLock lock(block->mesh_mutex);
5792 mesh_expired = block->getMeshExpired();
5794 // Mesh has not been expired and there is no mesh:
5795 // block has no content
5796 if(block->mesh == NULL && mesh_expired == false)
5800 f32 faraway = BS*50;
5801 //f32 faraway = m_control.wanted_range * BS;
5804 This has to be done with the mesh_mutex unlocked
5806 // Pretty random but this should work somewhat nicely
5807 if(mesh_expired && (
5808 (mesh_update_count < 3
5809 && (d < faraway || mesh_update_count < 2)
5812 (m_control.range_all && mesh_update_count < 20)
5815 /*if(mesh_expired && mesh_update_count < 6
5816 && (d < faraway || mesh_update_count < 3))*/
5818 mesh_update_count++;
5820 // Mesh has been expired: generate new mesh
5821 //block->updateMesh(daynight_ratio);
5822 m_client->addUpdateMeshTask(block->getPos());
5824 mesh_expired = false;
5829 Draw the faces of the block
5832 JMutexAutoLock lock(block->mesh_mutex);
5834 scene::SMesh *mesh = block->mesh;
5839 blocks_would_have_drawn++;
5840 if(blocks_drawn >= m_control.wanted_max_blocks
5841 && m_control.range_all == false
5842 && d > m_control.wanted_min_range * BS)
5846 u32 c = mesh->getMeshBufferCount();
5848 for(u32 i=0; i<c; i++)
5850 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
5851 const video::SMaterial& material = buf->getMaterial();
5852 video::IMaterialRenderer* rnd =
5853 driver->getMaterialRenderer(material.MaterialType);
5854 bool transparent = (rnd && rnd->isTransparent());
5855 // Render transparent on transparent pass and likewise.
5856 if(transparent == is_transparent_pass)
5859 This *shouldn't* hurt too much because Irrlicht
5860 doesn't change opengl textures if the old
5861 material is set again.
5863 driver->setMaterial(buf->getMaterial());
5864 driver->drawMeshBuffer(buf);
5865 vertex_count += buf->getVertexCount();
5869 } // foreach sectorblocks
5872 m_control.blocks_drawn = blocks_drawn;
5873 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
5875 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
5876 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
5879 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
5880 core::map<v3s16, MapBlock*> *affected_blocks)
5882 bool changed = false;
5884 Add it to all blocks touching it
5887 v3s16(0,0,0), // this
5888 v3s16(0,0,1), // back
5889 v3s16(0,1,0), // top
5890 v3s16(1,0,0), // right
5891 v3s16(0,0,-1), // front
5892 v3s16(0,-1,0), // bottom
5893 v3s16(-1,0,0), // left
5895 for(u16 i=0; i<7; i++)
5897 v3s16 p2 = p + dirs[i];
5898 // Block position of neighbor (or requested) node
5899 v3s16 blockpos = getNodeBlockPos(p2);
5900 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5901 if(blockref == NULL)
5903 // Relative position of requested node
5904 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5905 if(blockref->setTempMod(relpos, mod))
5910 if(changed && affected_blocks!=NULL)
5912 for(u16 i=0; i<7; i++)
5914 v3s16 p2 = p + dirs[i];
5915 // Block position of neighbor (or requested) node
5916 v3s16 blockpos = getNodeBlockPos(p2);
5917 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5918 if(blockref == NULL)
5920 affected_blocks->insert(blockpos, blockref);
5926 bool ClientMap::clearTempMod(v3s16 p,
5927 core::map<v3s16, MapBlock*> *affected_blocks)
5929 bool changed = false;
5931 v3s16(0,0,0), // this
5932 v3s16(0,0,1), // back
5933 v3s16(0,1,0), // top
5934 v3s16(1,0,0), // right
5935 v3s16(0,0,-1), // front
5936 v3s16(0,-1,0), // bottom
5937 v3s16(-1,0,0), // left
5939 for(u16 i=0; i<7; i++)
5941 v3s16 p2 = p + dirs[i];
5942 // Block position of neighbor (or requested) node
5943 v3s16 blockpos = getNodeBlockPos(p2);
5944 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5945 if(blockref == NULL)
5947 // Relative position of requested node
5948 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5949 if(blockref->clearTempMod(relpos))
5954 if(changed && affected_blocks!=NULL)
5956 for(u16 i=0; i<7; i++)
5958 v3s16 p2 = p + dirs[i];
5959 // Block position of neighbor (or requested) node
5960 v3s16 blockpos = getNodeBlockPos(p2);
5961 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5962 if(blockref == NULL)
5964 affected_blocks->insert(blockpos, blockref);
5970 void ClientMap::expireMeshes(bool only_daynight_diffed)
5972 TimeTaker timer("expireMeshes()");
5974 core::map<v2s16, MapSector*>::Iterator si;
5975 si = m_sectors.getIterator();
5976 for(; si.atEnd() == false; si++)
5978 MapSector *sector = si.getNode()->getValue();
5980 core::list< MapBlock * > sectorblocks;
5981 sector->getBlocks(sectorblocks);
5983 core::list< MapBlock * >::Iterator i;
5984 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5986 MapBlock *block = *i;
5988 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
5994 JMutexAutoLock lock(block->mesh_mutex);
5995 if(block->mesh != NULL)
5997 /*block->mesh->drop();
5998 block->mesh = NULL;*/
5999 block->setMeshExpired(true);
6006 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
6008 assert(mapType() == MAPTYPE_CLIENT);
6011 v3s16 p = blockpos + v3s16(0,0,0);
6012 MapBlock *b = getBlockNoCreate(p);
6013 b->updateMesh(daynight_ratio);
6014 //b->setMeshExpired(true);
6016 catch(InvalidPositionException &e){}
6019 v3s16 p = blockpos + v3s16(-1,0,0);
6020 MapBlock *b = getBlockNoCreate(p);
6021 b->updateMesh(daynight_ratio);
6022 //b->setMeshExpired(true);
6024 catch(InvalidPositionException &e){}
6026 v3s16 p = blockpos + v3s16(0,-1,0);
6027 MapBlock *b = getBlockNoCreate(p);
6028 b->updateMesh(daynight_ratio);
6029 //b->setMeshExpired(true);
6031 catch(InvalidPositionException &e){}
6033 v3s16 p = blockpos + v3s16(0,0,-1);
6034 MapBlock *b = getBlockNoCreate(p);
6035 b->updateMesh(daynight_ratio);
6036 //b->setMeshExpired(true);
6038 catch(InvalidPositionException &e){}
6043 Update mesh of block in which the node is, and if the node is at the
6044 leading edge, update the appropriate leading blocks too.
6046 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
6054 v3s16 blockposes[4];
6055 for(u32 i=0; i<4; i++)
6057 v3s16 np = nodepos + dirs[i];
6058 blockposes[i] = getNodeBlockPos(np);
6059 // Don't update mesh of block if it has been done already
6060 bool already_updated = false;
6061 for(u32 j=0; j<i; j++)
6063 if(blockposes[j] == blockposes[i])
6065 already_updated = true;
6072 MapBlock *b = getBlockNoCreate(blockposes[i]);
6073 b->updateMesh(daynight_ratio);
6078 void ClientMap::PrintInfo(std::ostream &out)
6089 MapVoxelManipulator::MapVoxelManipulator(Map *map)
6094 MapVoxelManipulator::~MapVoxelManipulator()
6096 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
6100 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
6102 TimeTaker timer1("emerge", &emerge_time);
6104 // Units of these are MapBlocks
6105 v3s16 p_min = getNodeBlockPos(a.MinEdge);
6106 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
6108 VoxelArea block_area_nodes
6109 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6111 addArea(block_area_nodes);
6113 for(s32 z=p_min.Z; z<=p_max.Z; z++)
6114 for(s32 y=p_min.Y; y<=p_max.Y; y++)
6115 for(s32 x=p_min.X; x<=p_max.X; x++)
6118 core::map<v3s16, bool>::Node *n;
6119 n = m_loaded_blocks.find(p);
6123 bool block_data_inexistent = false;
6126 TimeTaker timer1("emerge load", &emerge_load_time);
6128 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
6129 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
6132 dstream<<std::endl;*/
6134 MapBlock *block = m_map->getBlockNoCreate(p);
6135 if(block->isDummy())
6136 block_data_inexistent = true;
6138 block->copyTo(*this);
6140 catch(InvalidPositionException &e)
6142 block_data_inexistent = true;
6145 if(block_data_inexistent)
6147 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6148 // Fill with VOXELFLAG_INEXISTENT
6149 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
6150 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
6152 s32 i = m_area.index(a.MinEdge.X,y,z);
6153 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
6157 m_loaded_blocks.insert(p, !block_data_inexistent);
6160 //dstream<<"emerge done"<<std::endl;
6164 SUGG: Add an option to only update eg. water and air nodes.
6165 This will make it interfere less with important stuff if
6168 void MapVoxelManipulator::blitBack
6169 (core::map<v3s16, MapBlock*> & modified_blocks)
6171 if(m_area.getExtent() == v3s16(0,0,0))
6174 //TimeTaker timer1("blitBack");
6176 /*dstream<<"blitBack(): m_loaded_blocks.size()="
6177 <<m_loaded_blocks.size()<<std::endl;*/
6180 Initialize block cache
6182 v3s16 blockpos_last;
6183 MapBlock *block = NULL;
6184 bool block_checked_in_modified = false;
6186 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
6187 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
6188 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
6192 u8 f = m_flags[m_area.index(p)];
6193 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
6196 MapNode &n = m_data[m_area.index(p)];
6198 v3s16 blockpos = getNodeBlockPos(p);
6203 if(block == NULL || blockpos != blockpos_last){
6204 block = m_map->getBlockNoCreate(blockpos);
6205 blockpos_last = blockpos;
6206 block_checked_in_modified = false;
6209 // Calculate relative position in block
6210 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
6212 // Don't continue if nothing has changed here
6213 if(block->getNode(relpos) == n)
6216 //m_map->setNode(m_area.MinEdge + p, n);
6217 block->setNode(relpos, n);
6220 Make sure block is in modified_blocks
6222 if(block_checked_in_modified == false)
6224 modified_blocks[blockpos] = block;
6225 block_checked_in_modified = true;
6228 catch(InvalidPositionException &e)
6234 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
6235 MapVoxelManipulator(map),
6236 m_create_area(false)
6240 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
6244 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
6246 // Just create the area so that it can be pointed to
6247 VoxelManipulator::emerge(a, caller_id);
6250 void ManualMapVoxelManipulator::initialEmerge(
6251 v3s16 blockpos_min, v3s16 blockpos_max)
6253 TimeTaker timer1("initialEmerge", &emerge_time);
6255 // Units of these are MapBlocks
6256 v3s16 p_min = blockpos_min;
6257 v3s16 p_max = blockpos_max;
6259 VoxelArea block_area_nodes
6260 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6262 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
6265 dstream<<"initialEmerge: area: ";
6266 block_area_nodes.print(dstream);
6267 dstream<<" ("<<size_MB<<"MB)";
6271 addArea(block_area_nodes);
6273 for(s32 z=p_min.Z; z<=p_max.Z; z++)
6274 for(s32 y=p_min.Y; y<=p_max.Y; y++)
6275 for(s32 x=p_min.X; x<=p_max.X; x++)
6278 core::map<v3s16, bool>::Node *n;
6279 n = m_loaded_blocks.find(p);
6283 bool block_data_inexistent = false;
6286 TimeTaker timer1("emerge load", &emerge_load_time);
6288 MapBlock *block = m_map->getBlockNoCreate(p);
6289 if(block->isDummy())
6290 block_data_inexistent = true;
6292 block->copyTo(*this);
6294 catch(InvalidPositionException &e)
6296 block_data_inexistent = true;
6299 if(block_data_inexistent)
6302 Mark area inexistent
6304 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6305 // Fill with VOXELFLAG_INEXISTENT
6306 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
6307 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
6309 s32 i = m_area.index(a.MinEdge.X,y,z);
6310 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
6314 m_loaded_blocks.insert(p, !block_data_inexistent);
6318 void ManualMapVoxelManipulator::blitBackAll(
6319 core::map<v3s16, MapBlock*> * modified_blocks)
6321 if(m_area.getExtent() == v3s16(0,0,0))
6325 Copy data of all blocks
6327 for(core::map<v3s16, bool>::Iterator
6328 i = m_loaded_blocks.getIterator();
6329 i.atEnd() == false; i++)
6331 bool existed = i.getNode()->getValue();
6332 if(existed == false)
6334 v3s16 p = i.getNode()->getKey();
6335 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
6338 dstream<<"WARNING: "<<__FUNCTION_NAME
6339 <<": got NULL block "
6340 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
6345 block->copyFrom(*this);
6348 modified_blocks->insert(p, block);