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"
36 Map::Map(std::ostream &dout):
40 /*m_sector_mutex.Init();
41 assert(m_sector_mutex.IsInitialized());*/
49 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
50 for(; i.atEnd() == false; i++)
52 MapSector *sector = i.getNode()->getValue();
57 void Map::addEventReceiver(MapEventReceiver *event_receiver)
59 m_event_receivers.insert(event_receiver, false);
62 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
64 if(m_event_receivers.find(event_receiver) == NULL)
66 m_event_receivers.remove(event_receiver);
69 void Map::dispatchEvent(MapEditEvent *event)
71 for(core::map<MapEventReceiver*, bool>::Iterator
72 i = m_event_receivers.getIterator();
73 i.atEnd()==false; i++)
75 MapEventReceiver* event_receiver = i.getNode()->getKey();
76 event_receiver->onMapEditEvent(event);
80 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
82 if(m_sector_cache != NULL && p == m_sector_cache_p){
83 MapSector * sector = m_sector_cache;
84 // Reset inactivity timer
85 sector->usage_timer = 0.0;
89 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
94 MapSector *sector = n->getValue();
96 // Cache the last result
98 m_sector_cache = sector;
100 // Reset inactivity timer
101 sector->usage_timer = 0.0;
105 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
107 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
109 return getSectorNoGenerateNoExNoLock(p);
112 MapSector * Map::getSectorNoGenerate(v2s16 p)
114 MapSector *sector = getSectorNoGenerateNoEx(p);
116 throw InvalidPositionException();
121 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
123 v2s16 p2d(p3d.X, p3d.Z);
124 MapSector * sector = getSectorNoGenerate(p2d);
126 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
131 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
135 v2s16 p2d(p3d.X, p3d.Z);
136 MapSector * sector = getSectorNoGenerate(p2d);
137 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
140 catch(InvalidPositionException &e)
146 /*MapBlock * Map::getBlockCreate(v3s16 p3d)
148 v2s16 p2d(p3d.X, p3d.Z);
149 MapSector * sector = getSectorCreate(p2d);
151 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
154 block = sector->createBlankBlock(p3d.Y);
158 bool Map::isNodeUnderground(v3s16 p)
160 v3s16 blockpos = getNodeBlockPos(p);
162 MapBlock * block = getBlockNoCreate(blockpos);
163 return block->getIsUnderground();
165 catch(InvalidPositionException &e)
172 Goes recursively through the neighbours of the node.
174 Alters only transparent nodes.
176 If the lighting of the neighbour is lower than the lighting of
177 the node was (before changing it to 0 at the step before), the
178 lighting of the neighbour is set to 0 and then the same stuff
179 repeats for the neighbour.
181 The ending nodes of the routine are stored in light_sources.
182 This is useful when a light is removed. In such case, this
183 routine can be called for the light node and then again for
184 light_sources to re-light the area without the removed light.
186 values of from_nodes are lighting values.
188 void Map::unspreadLight(enum LightBank bank,
189 core::map<v3s16, u8> & from_nodes,
190 core::map<v3s16, bool> & light_sources,
191 core::map<v3s16, MapBlock*> & modified_blocks)
194 v3s16(0,0,1), // back
196 v3s16(1,0,0), // right
197 v3s16(0,0,-1), // front
198 v3s16(0,-1,0), // bottom
199 v3s16(-1,0,0), // left
202 if(from_nodes.size() == 0)
205 u32 blockchangecount = 0;
207 core::map<v3s16, u8> unlighted_nodes;
208 core::map<v3s16, u8>::Iterator j;
209 j = from_nodes.getIterator();
212 Initialize block cache
215 MapBlock *block = NULL;
216 // Cache this a bit, too
217 bool block_checked_in_modified = false;
219 for(; j.atEnd() == false; j++)
221 v3s16 pos = j.getNode()->getKey();
222 v3s16 blockpos = getNodeBlockPos(pos);
224 // Only fetch a new block if the block position has changed
226 if(block == NULL || blockpos != blockpos_last){
227 block = getBlockNoCreate(blockpos);
228 blockpos_last = blockpos;
230 block_checked_in_modified = false;
234 catch(InvalidPositionException &e)
242 // Calculate relative position in block
243 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
245 // Get node straight from the block
246 MapNode n = block->getNode(relpos);
248 u8 oldlight = j.getNode()->getValue();
250 // Loop through 6 neighbors
251 for(u16 i=0; i<6; i++)
253 // Get the position of the neighbor node
254 v3s16 n2pos = pos + dirs[i];
256 // Get the block where the node is located
257 v3s16 blockpos = getNodeBlockPos(n2pos);
261 // Only fetch a new block if the block position has changed
263 if(block == NULL || blockpos != blockpos_last){
264 block = getBlockNoCreate(blockpos);
265 blockpos_last = blockpos;
267 block_checked_in_modified = false;
271 catch(InvalidPositionException &e)
276 // Calculate relative position in block
277 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
278 // Get node straight from the block
279 MapNode n2 = block->getNode(relpos);
281 bool changed = false;
283 //TODO: Optimize output by optimizing light_sources?
286 If the neighbor is dimmer than what was specified
287 as oldlight (the light of the previous node)
289 if(n2.getLight(bank) < oldlight)
292 And the neighbor is transparent and it has some light
294 if(n2.light_propagates() && n2.getLight(bank) != 0)
297 Set light to 0 and add to queue
300 u8 current_light = n2.getLight(bank);
301 n2.setLight(bank, 0);
302 block->setNode(relpos, n2);
304 unlighted_nodes.insert(n2pos, current_light);
308 Remove from light_sources if it is there
309 NOTE: This doesn't happen nearly at all
311 /*if(light_sources.find(n2pos))
313 std::cout<<"Removed from light_sources"<<std::endl;
314 light_sources.remove(n2pos);
319 if(light_sources.find(n2pos) != NULL)
320 light_sources.remove(n2pos);*/
323 light_sources.insert(n2pos, true);
326 // Add to modified_blocks
327 if(changed == true && block_checked_in_modified == false)
329 // If the block is not found in modified_blocks, add.
330 if(modified_blocks.find(blockpos) == NULL)
332 modified_blocks.insert(blockpos, block);
334 block_checked_in_modified = true;
337 catch(InvalidPositionException &e)
344 /*dstream<<"unspreadLight(): Changed block "
345 <<blockchangecount<<" times"
346 <<" for "<<from_nodes.size()<<" nodes"
349 if(unlighted_nodes.size() > 0)
350 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
354 A single-node wrapper of the above
356 void Map::unLightNeighbors(enum LightBank bank,
357 v3s16 pos, u8 lightwas,
358 core::map<v3s16, bool> & light_sources,
359 core::map<v3s16, MapBlock*> & modified_blocks)
361 core::map<v3s16, u8> from_nodes;
362 from_nodes.insert(pos, lightwas);
364 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
368 Lights neighbors of from_nodes, collects all them and then
371 void Map::spreadLight(enum LightBank bank,
372 core::map<v3s16, bool> & from_nodes,
373 core::map<v3s16, MapBlock*> & modified_blocks)
375 const v3s16 dirs[6] = {
376 v3s16(0,0,1), // back
378 v3s16(1,0,0), // right
379 v3s16(0,0,-1), // front
380 v3s16(0,-1,0), // bottom
381 v3s16(-1,0,0), // left
384 if(from_nodes.size() == 0)
387 u32 blockchangecount = 0;
389 core::map<v3s16, bool> lighted_nodes;
390 core::map<v3s16, bool>::Iterator j;
391 j = from_nodes.getIterator();
394 Initialize block cache
397 MapBlock *block = NULL;
398 // Cache this a bit, too
399 bool block_checked_in_modified = false;
401 for(; j.atEnd() == false; j++)
402 //for(; j != from_nodes.end(); j++)
404 v3s16 pos = j.getNode()->getKey();
406 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
407 v3s16 blockpos = getNodeBlockPos(pos);
409 // Only fetch a new block if the block position has changed
411 if(block == NULL || blockpos != blockpos_last){
412 block = getBlockNoCreate(blockpos);
413 blockpos_last = blockpos;
415 block_checked_in_modified = false;
419 catch(InvalidPositionException &e)
427 // Calculate relative position in block
428 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
430 // Get node straight from the block
431 MapNode n = block->getNode(relpos);
433 u8 oldlight = n.getLight(bank);
434 u8 newlight = diminish_light(oldlight);
436 // Loop through 6 neighbors
437 for(u16 i=0; i<6; i++){
438 // Get the position of the neighbor node
439 v3s16 n2pos = pos + dirs[i];
441 // Get the block where the node is located
442 v3s16 blockpos = getNodeBlockPos(n2pos);
446 // Only fetch a new block if the block position has changed
448 if(block == NULL || blockpos != blockpos_last){
449 block = getBlockNoCreate(blockpos);
450 blockpos_last = blockpos;
452 block_checked_in_modified = false;
456 catch(InvalidPositionException &e)
461 // Calculate relative position in block
462 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
463 // Get node straight from the block
464 MapNode n2 = block->getNode(relpos);
466 bool changed = false;
468 If the neighbor is brighter than the current node,
469 add to list (it will light up this node on its turn)
471 if(n2.getLight(bank) > undiminish_light(oldlight))
473 lighted_nodes.insert(n2pos, true);
474 //lighted_nodes.push_back(n2pos);
478 If the neighbor is dimmer than how much light this node
479 would spread on it, add to list
481 if(n2.getLight(bank) < newlight)
483 if(n2.light_propagates())
485 n2.setLight(bank, newlight);
486 block->setNode(relpos, n2);
487 lighted_nodes.insert(n2pos, true);
488 //lighted_nodes.push_back(n2pos);
493 // Add to modified_blocks
494 if(changed == true && block_checked_in_modified == false)
496 // If the block is not found in modified_blocks, add.
497 if(modified_blocks.find(blockpos) == NULL)
499 modified_blocks.insert(blockpos, block);
501 block_checked_in_modified = true;
504 catch(InvalidPositionException &e)
511 /*dstream<<"spreadLight(): Changed block "
512 <<blockchangecount<<" times"
513 <<" for "<<from_nodes.size()<<" nodes"
516 if(lighted_nodes.size() > 0)
517 spreadLight(bank, lighted_nodes, modified_blocks);
521 A single-node source variation of the above.
523 void Map::lightNeighbors(enum LightBank bank,
525 core::map<v3s16, MapBlock*> & modified_blocks)
527 core::map<v3s16, bool> from_nodes;
528 from_nodes.insert(pos, true);
529 spreadLight(bank, from_nodes, modified_blocks);
532 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
535 v3s16(0,0,1), // back
537 v3s16(1,0,0), // right
538 v3s16(0,0,-1), // front
539 v3s16(0,-1,0), // bottom
540 v3s16(-1,0,0), // left
543 u8 brightest_light = 0;
544 v3s16 brightest_pos(0,0,0);
545 bool found_something = false;
547 // Loop through 6 neighbors
548 for(u16 i=0; i<6; i++){
549 // Get the position of the neighbor node
550 v3s16 n2pos = p + dirs[i];
555 catch(InvalidPositionException &e)
559 if(n2.getLight(bank) > brightest_light || found_something == false){
560 brightest_light = n2.getLight(bank);
561 brightest_pos = n2pos;
562 found_something = true;
566 if(found_something == false)
567 throw InvalidPositionException();
569 return brightest_pos;
573 Propagates sunlight down from a node.
574 Starting point gets sunlight.
576 Returns the lowest y value of where the sunlight went.
578 Mud is turned into grass in where the sunlight stops.
580 s16 Map::propagateSunlight(v3s16 start,
581 core::map<v3s16, MapBlock*> & modified_blocks)
586 v3s16 pos(start.X, y, start.Z);
588 v3s16 blockpos = getNodeBlockPos(pos);
591 block = getBlockNoCreate(blockpos);
593 catch(InvalidPositionException &e)
598 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
599 MapNode n = block->getNode(relpos);
601 if(n.sunlight_propagates())
603 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
604 block->setNode(relpos, n);
606 modified_blocks.insert(blockpos, block);
610 /*// Turn mud into grass
611 if(n.d == CONTENT_MUD)
614 block->setNode(relpos, n);
615 modified_blocks.insert(blockpos, block);
618 // Sunlight goes no further
625 void Map::updateLighting(enum LightBank bank,
626 core::map<v3s16, MapBlock*> & a_blocks,
627 core::map<v3s16, MapBlock*> & modified_blocks)
629 /*m_dout<<DTIME<<"Map::updateLighting(): "
630 <<a_blocks.size()<<" blocks."<<std::endl;*/
632 //TimeTaker timer("updateLighting");
636 //u32 count_was = modified_blocks.size();
638 core::map<v3s16, MapBlock*> blocks_to_update;
640 core::map<v3s16, bool> light_sources;
642 core::map<v3s16, u8> unlight_from;
644 core::map<v3s16, MapBlock*>::Iterator i;
645 i = a_blocks.getIterator();
646 for(; i.atEnd() == false; i++)
648 MapBlock *block = i.getNode()->getValue();
652 // Don't bother with dummy blocks.
656 v3s16 pos = block->getPos();
657 modified_blocks.insert(pos, block);
659 blocks_to_update.insert(pos, block);
662 Clear all light from block
664 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
665 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
666 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
671 MapNode n = block->getNode(v3s16(x,y,z));
672 u8 oldlight = n.getLight(bank);
674 block->setNode(v3s16(x,y,z), n);
676 // Collect borders for unlighting
677 if(x==0 || x == MAP_BLOCKSIZE-1
678 || y==0 || y == MAP_BLOCKSIZE-1
679 || z==0 || z == MAP_BLOCKSIZE-1)
681 v3s16 p_map = p + v3s16(
684 MAP_BLOCKSIZE*pos.Z);
685 unlight_from.insert(p_map, oldlight);
688 catch(InvalidPositionException &e)
691 This would happen when dealing with a
695 dstream<<"updateLighting(): InvalidPositionException"
700 if(bank == LIGHTBANK_DAY)
702 bool bottom_valid = block->propagateSunlight(light_sources);
704 // If bottom is valid, we're done.
708 else if(bank == LIGHTBANK_NIGHT)
710 // For night lighting, sunlight is not propagated
715 // Invalid lighting bank
719 /*dstream<<"Bottom for sunlight-propagated block ("
720 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
723 // Bottom sunlight is not valid; get the block and loop to it
727 block = getBlockNoCreate(pos);
729 catch(InvalidPositionException &e)
739 TimeTaker timer("unspreadLight");
740 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
745 u32 diff = modified_blocks.size() - count_was;
746 count_was = modified_blocks.size();
747 dstream<<"unspreadLight modified "<<diff<<std::endl;
751 TimeTaker timer("spreadLight");
752 spreadLight(bank, light_sources, modified_blocks);
757 u32 diff = modified_blocks.size() - count_was;
758 count_was = modified_blocks.size();
759 dstream<<"spreadLight modified "<<diff<<std::endl;
764 //MapVoxelManipulator vmanip(this);
766 // Make a manual voxel manipulator and load all the blocks
767 // that touch the requested blocks
768 ManualMapVoxelManipulator vmanip(this);
769 core::map<v3s16, MapBlock*>::Iterator i;
770 i = blocks_to_update.getIterator();
771 for(; i.atEnd() == false; i++)
773 MapBlock *block = i.getNode()->getValue();
774 v3s16 p = block->getPos();
776 // Add all surrounding blocks
777 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
780 Add all surrounding blocks that have up-to-date lighting
781 NOTE: This doesn't quite do the job (not everything
782 appropriate is lighted)
784 /*for(s16 z=-1; z<=1; z++)
785 for(s16 y=-1; y<=1; y++)
786 for(s16 x=-1; x<=1; x++)
789 MapBlock *block = getBlockNoCreateNoEx(p);
794 if(block->getLightingExpired())
796 vmanip.initialEmerge(p, p);
799 // Lighting of block will be updated completely
800 block->setLightingExpired(false);
804 //TimeTaker timer("unSpreadLight");
805 vmanip.unspreadLight(bank, unlight_from, light_sources);
808 //TimeTaker timer("spreadLight");
809 vmanip.spreadLight(bank, light_sources);
812 //TimeTaker timer("blitBack");
813 vmanip.blitBack(modified_blocks);
815 /*dstream<<"emerge_time="<<emerge_time<<std::endl;
819 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
822 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
823 core::map<v3s16, MapBlock*> & modified_blocks)
825 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
826 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
829 Update information about whether day and night light differ
831 for(core::map<v3s16, MapBlock*>::Iterator
832 i = modified_blocks.getIterator();
833 i.atEnd() == false; i++)
835 MapBlock *block = i.getNode()->getValue();
836 block->updateDayNightDiff();
842 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
843 core::map<v3s16, MapBlock*> &modified_blocks)
846 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
847 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
850 From this node to nodes underneath:
851 If lighting is sunlight (1.0), unlight neighbours and
856 v3s16 toppos = p + v3s16(0,1,0);
857 v3s16 bottompos = p + v3s16(0,-1,0);
859 bool node_under_sunlight = true;
860 core::map<v3s16, bool> light_sources;
863 If there is a node at top and it doesn't have sunlight,
864 there has not been any sunlight going down.
866 Otherwise there probably is.
869 MapNode topnode = getNode(toppos);
871 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
872 node_under_sunlight = false;
874 catch(InvalidPositionException &e)
880 If the new node is solid and there is grass below, change it to mud
882 if(content_features(n.d).walkable == true)
885 MapNode bottomnode = getNode(bottompos);
887 if(bottomnode.d == CONTENT_GRASS
888 || bottomnode.d == CONTENT_GRASS_FOOTSTEPS)
890 bottomnode.d = CONTENT_MUD;
891 setNode(bottompos, bottomnode);
894 catch(InvalidPositionException &e)
902 If the new node is mud and it is under sunlight, change it
905 if(n.d == CONTENT_MUD && node_under_sunlight)
912 Remove all light that has come out of this node
915 enum LightBank banks[] =
920 for(s32 i=0; i<2; i++)
922 enum LightBank bank = banks[i];
924 u8 lightwas = getNode(p).getLight(bank);
926 // Add the block of the added node to modified_blocks
927 v3s16 blockpos = getNodeBlockPos(p);
928 MapBlock * block = getBlockNoCreate(blockpos);
929 assert(block != NULL);
930 modified_blocks.insert(blockpos, block);
932 assert(isValidPosition(p));
934 // Unlight neighbours of node.
935 // This means setting light of all consequent dimmer nodes
937 // This also collects the nodes at the border which will spread
938 // light again into this.
939 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
945 If node lets sunlight through and is under sunlight, it has
948 if(node_under_sunlight && content_features(n.d).sunlight_propagates)
950 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
954 Set the node on the map
963 NodeMetadata *meta_proto = content_features(n.d).initial_metadata;
966 NodeMetadata *meta = meta_proto->clone();
967 setNodeMetadata(p, meta);
971 If node is under sunlight and doesn't let sunlight through,
972 take all sunlighted nodes under it and clear light from them
973 and from where the light has been spread.
974 TODO: This could be optimized by mass-unlighting instead
977 if(node_under_sunlight && !content_features(n.d).sunlight_propagates)
981 //m_dout<<DTIME<<"y="<<y<<std::endl;
982 v3s16 n2pos(p.X, y, p.Z);
988 catch(InvalidPositionException &e)
993 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
995 unLightNeighbors(LIGHTBANK_DAY,
996 n2pos, n2.getLight(LIGHTBANK_DAY),
997 light_sources, modified_blocks);
998 n2.setLight(LIGHTBANK_DAY, 0);
1006 for(s32 i=0; i<2; i++)
1008 enum LightBank bank = banks[i];
1011 Spread light from all nodes that might be capable of doing so
1013 spreadLight(bank, light_sources, modified_blocks);
1017 Update information about whether day and night light differ
1019 for(core::map<v3s16, MapBlock*>::Iterator
1020 i = modified_blocks.getIterator();
1021 i.atEnd() == false; i++)
1023 MapBlock *block = i.getNode()->getValue();
1024 block->updateDayNightDiff();
1028 Add neighboring liquid nodes and the node itself if it is
1029 liquid (=water node was added) to transform queue.
1032 v3s16(0,0,0), // self
1033 v3s16(0,0,1), // back
1034 v3s16(0,1,0), // top
1035 v3s16(1,0,0), // right
1036 v3s16(0,0,-1), // front
1037 v3s16(0,-1,0), // bottom
1038 v3s16(-1,0,0), // left
1040 for(u16 i=0; i<7; i++)
1045 v3s16 p2 = p + dirs[i];
1047 MapNode n2 = getNode(p2);
1048 if(content_liquid(n2.d))
1050 m_transforming_liquid.push_back(p2);
1053 }catch(InvalidPositionException &e)
1061 void Map::removeNodeAndUpdate(v3s16 p,
1062 core::map<v3s16, MapBlock*> &modified_blocks)
1064 /*PrintInfo(m_dout);
1065 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1066 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1068 bool node_under_sunlight = true;
1070 v3s16 toppos = p + v3s16(0,1,0);
1072 // Node will be replaced with this
1073 u8 replace_material = CONTENT_AIR;
1076 If there is a node at top and it doesn't have sunlight,
1077 there will be no sunlight going down.
1080 MapNode topnode = getNode(toppos);
1082 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1083 node_under_sunlight = false;
1085 catch(InvalidPositionException &e)
1089 core::map<v3s16, bool> light_sources;
1091 enum LightBank banks[] =
1096 for(s32 i=0; i<2; i++)
1098 enum LightBank bank = banks[i];
1101 Unlight neighbors (in case the node is a light source)
1103 unLightNeighbors(bank, p,
1104 getNode(p).getLight(bank),
1105 light_sources, modified_blocks);
1109 Remove node metadata
1112 removeNodeMetadata(p);
1116 This also clears the lighting.
1120 n.d = replace_material;
1123 for(s32 i=0; i<2; i++)
1125 enum LightBank bank = banks[i];
1128 Recalculate lighting
1130 spreadLight(bank, light_sources, modified_blocks);
1133 // Add the block of the removed node to modified_blocks
1134 v3s16 blockpos = getNodeBlockPos(p);
1135 MapBlock * block = getBlockNoCreate(blockpos);
1136 assert(block != NULL);
1137 modified_blocks.insert(blockpos, block);
1140 If the removed node was under sunlight, propagate the
1141 sunlight down from it and then light all neighbors
1142 of the propagated blocks.
1144 if(node_under_sunlight)
1146 s16 ybottom = propagateSunlight(p, modified_blocks);
1147 /*m_dout<<DTIME<<"Node was under sunlight. "
1148 "Propagating sunlight";
1149 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1151 for(; y >= ybottom; y--)
1153 v3s16 p2(p.X, y, p.Z);
1154 /*m_dout<<DTIME<<"lighting neighbors of node ("
1155 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1157 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1162 // Set the lighting of this node to 0
1163 // TODO: Is this needed? Lighting is cleared up there already.
1165 MapNode n = getNode(p);
1166 n.setLight(LIGHTBANK_DAY, 0);
1169 catch(InvalidPositionException &e)
1175 for(s32 i=0; i<2; i++)
1177 enum LightBank bank = banks[i];
1179 // Get the brightest neighbour node and propagate light from it
1180 v3s16 n2p = getBrightestNeighbour(bank, p);
1182 MapNode n2 = getNode(n2p);
1183 lightNeighbors(bank, n2p, modified_blocks);
1185 catch(InvalidPositionException &e)
1191 Update information about whether day and night light differ
1193 for(core::map<v3s16, MapBlock*>::Iterator
1194 i = modified_blocks.getIterator();
1195 i.atEnd() == false; i++)
1197 MapBlock *block = i.getNode()->getValue();
1198 block->updateDayNightDiff();
1202 Add neighboring liquid nodes to transform queue.
1205 v3s16(0,0,1), // back
1206 v3s16(0,1,0), // top
1207 v3s16(1,0,0), // right
1208 v3s16(0,0,-1), // front
1209 v3s16(0,-1,0), // bottom
1210 v3s16(-1,0,0), // left
1212 for(u16 i=0; i<6; i++)
1217 v3s16 p2 = p + dirs[i];
1219 MapNode n2 = getNode(p2);
1220 if(content_liquid(n2.d))
1222 m_transforming_liquid.push_back(p2);
1225 }catch(InvalidPositionException &e)
1231 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1234 event.type = MEET_ADDNODE;
1238 bool succeeded = true;
1240 core::map<v3s16, MapBlock*> modified_blocks;
1241 addNodeAndUpdate(p, n, modified_blocks);
1243 // Copy modified_blocks to event
1244 for(core::map<v3s16, MapBlock*>::Iterator
1245 i = modified_blocks.getIterator();
1246 i.atEnd()==false; i++)
1248 event.modified_blocks.insert(i.getNode()->getKey(), false);
1251 catch(InvalidPositionException &e){
1255 dispatchEvent(&event);
1260 bool Map::removeNodeWithEvent(v3s16 p)
1263 event.type = MEET_REMOVENODE;
1266 bool succeeded = true;
1268 core::map<v3s16, MapBlock*> modified_blocks;
1269 removeNodeAndUpdate(p, modified_blocks);
1271 // Copy modified_blocks to event
1272 for(core::map<v3s16, MapBlock*>::Iterator
1273 i = modified_blocks.getIterator();
1274 i.atEnd()==false; i++)
1276 event.modified_blocks.insert(i.getNode()->getKey(), false);
1279 catch(InvalidPositionException &e){
1283 dispatchEvent(&event);
1288 bool Map::dayNightDiffed(v3s16 blockpos)
1291 v3s16 p = blockpos + v3s16(0,0,0);
1292 MapBlock *b = getBlockNoCreate(p);
1293 if(b->dayNightDiffed())
1296 catch(InvalidPositionException &e){}
1299 v3s16 p = blockpos + v3s16(-1,0,0);
1300 MapBlock *b = getBlockNoCreate(p);
1301 if(b->dayNightDiffed())
1304 catch(InvalidPositionException &e){}
1306 v3s16 p = blockpos + v3s16(0,-1,0);
1307 MapBlock *b = getBlockNoCreate(p);
1308 if(b->dayNightDiffed())
1311 catch(InvalidPositionException &e){}
1313 v3s16 p = blockpos + v3s16(0,0,-1);
1314 MapBlock *b = getBlockNoCreate(p);
1315 if(b->dayNightDiffed())
1318 catch(InvalidPositionException &e){}
1321 v3s16 p = blockpos + v3s16(1,0,0);
1322 MapBlock *b = getBlockNoCreate(p);
1323 if(b->dayNightDiffed())
1326 catch(InvalidPositionException &e){}
1328 v3s16 p = blockpos + v3s16(0,1,0);
1329 MapBlock *b = getBlockNoCreate(p);
1330 if(b->dayNightDiffed())
1333 catch(InvalidPositionException &e){}
1335 v3s16 p = blockpos + v3s16(0,0,1);
1336 MapBlock *b = getBlockNoCreate(p);
1337 if(b->dayNightDiffed())
1340 catch(InvalidPositionException &e){}
1346 Updates usage timers
1348 void Map::timerUpdate(float dtime)
1350 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
1352 core::map<v2s16, MapSector*>::Iterator si;
1354 si = m_sectors.getIterator();
1355 for(; si.atEnd() == false; si++)
1357 MapSector *sector = si.getNode()->getValue();
1358 sector->usage_timer += dtime;
1362 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1365 Wait for caches to be removed before continuing.
1367 This disables the existence of caches while locked
1369 //SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
1371 core::list<v2s16>::Iterator j;
1372 for(j=list.begin(); j!=list.end(); j++)
1374 MapSector *sector = m_sectors[*j];
1377 sector->deleteBlocks();
1382 If sector is in sector cache, remove it from there
1384 if(m_sector_cache == sector)
1386 m_sector_cache = NULL;
1389 Remove from map and delete
1391 m_sectors.remove(*j);
1397 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1398 core::list<v3s16> *deleted_blocks)
1400 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
1402 core::list<v2s16> sector_deletion_queue;
1403 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1404 for(; i.atEnd() == false; i++)
1406 MapSector *sector = i.getNode()->getValue();
1408 Delete sector from memory if it hasn't been used in a long time
1410 if(sector->usage_timer > timeout)
1412 sector_deletion_queue.push_back(i.getNode()->getKey());
1414 if(deleted_blocks != NULL)
1416 // Collect positions of blocks of sector
1417 MapSector *sector = i.getNode()->getValue();
1418 core::list<MapBlock*> blocks;
1419 sector->getBlocks(blocks);
1420 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1421 i != blocks.end(); i++)
1423 deleted_blocks->push_back((*i)->getPos());
1428 deleteSectors(sector_deletion_queue, only_blocks);
1429 return sector_deletion_queue.getSize();
1432 void Map::PrintInfo(std::ostream &out)
1437 #define WATER_DROP_BOOST 4
1439 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1441 DSTACK(__FUNCTION_NAME);
1442 //TimeTaker timer("transformLiquids()");
1445 u32 initial_size = m_transforming_liquid.size();
1447 /*if(initial_size != 0)
1448 dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1450 while(m_transforming_liquid.size() != 0)
1453 Get a queued transforming liquid node
1455 v3s16 p0 = m_transforming_liquid.pop_front();
1457 MapNode n0 = getNode(p0);
1459 // Don't deal with non-liquids
1460 if(content_liquid(n0.d) == false)
1463 bool is_source = !content_flowing_liquid(n0.d);
1465 u8 liquid_level = 8;
1466 if(is_source == false)
1467 liquid_level = n0.param2 & 0x0f;
1469 // Turn possible source into non-source
1470 u8 nonsource_c = make_liquid_flowing(n0.d);
1473 If not source, check that some node flows into this one
1474 and what is the level of liquid in this one
1476 if(is_source == false)
1478 s8 new_liquid_level_max = -1;
1480 v3s16 dirs_from[5] = {
1481 v3s16(0,1,0), // top
1482 v3s16(0,0,1), // back
1483 v3s16(1,0,0), // right
1484 v3s16(0,0,-1), // front
1485 v3s16(-1,0,0), // left
1487 for(u16 i=0; i<5; i++)
1492 bool from_top = (i==0);
1494 v3s16 p2 = p0 + dirs_from[i];
1495 MapNode n2 = getNode(p2);
1497 if(content_liquid(n2.d))
1499 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1500 // Check that the liquids are the same type
1501 if(n2_nonsource_c != nonsource_c)
1503 dstream<<"WARNING: Not handling: different liquids"
1504 " collide"<<std::endl;
1507 bool n2_is_source = !content_flowing_liquid(n2.d);
1508 s8 n2_liquid_level = 8;
1509 if(n2_is_source == false)
1510 n2_liquid_level = n2.param2 & 0x07;
1512 s8 new_liquid_level = -1;
1515 //new_liquid_level = 7;
1516 if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
1517 new_liquid_level = 7;
1519 new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
1521 else if(n2_liquid_level > 0)
1523 new_liquid_level = n2_liquid_level - 1;
1526 if(new_liquid_level > new_liquid_level_max)
1527 new_liquid_level_max = new_liquid_level;
1530 }catch(InvalidPositionException &e)
1536 If liquid level should be something else, update it and
1537 add all the neighboring water nodes to the transform queue.
1539 if(new_liquid_level_max != liquid_level)
1541 if(new_liquid_level_max == -1)
1543 // Remove water alltoghether
1550 n0.param2 = new_liquid_level_max;
1554 // Block has been modified
1556 v3s16 blockpos = getNodeBlockPos(p0);
1557 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1559 modified_blocks.insert(blockpos, block);
1563 Add neighboring non-source liquid nodes to transform queue.
1566 v3s16(0,0,1), // back
1567 v3s16(0,1,0), // top
1568 v3s16(1,0,0), // right
1569 v3s16(0,0,-1), // front
1570 v3s16(0,-1,0), // bottom
1571 v3s16(-1,0,0), // left
1573 for(u16 i=0; i<6; i++)
1578 v3s16 p2 = p0 + dirs[i];
1580 MapNode n2 = getNode(p2);
1581 if(content_flowing_liquid(n2.d))
1583 m_transforming_liquid.push_back(p2);
1586 }catch(InvalidPositionException &e)
1593 // Get a new one from queue if the node has turned into non-water
1594 if(content_liquid(n0.d) == false)
1598 Flow water from this node
1600 v3s16 dirs_to[5] = {
1601 v3s16(0,-1,0), // bottom
1602 v3s16(0,0,1), // back
1603 v3s16(1,0,0), // right
1604 v3s16(0,0,-1), // front
1605 v3s16(-1,0,0), // left
1607 for(u16 i=0; i<5; i++)
1612 bool to_bottom = (i == 0);
1614 // If liquid is at lowest possible height, it's not going
1615 // anywhere except down
1616 if(liquid_level == 0 && to_bottom == false)
1619 u8 liquid_next_level = 0;
1620 // If going to bottom
1623 //liquid_next_level = 7;
1624 if(liquid_level >= 7 - WATER_DROP_BOOST)
1625 liquid_next_level = 7;
1627 liquid_next_level = liquid_level + WATER_DROP_BOOST;
1630 liquid_next_level = liquid_level - 1;
1632 bool n2_changed = false;
1633 bool flowed = false;
1635 v3s16 p2 = p0 + dirs_to[i];
1637 MapNode n2 = getNode(p2);
1638 //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
1640 if(content_liquid(n2.d))
1642 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1643 // Check that the liquids are the same type
1644 if(n2_nonsource_c != nonsource_c)
1646 dstream<<"WARNING: Not handling: different liquids"
1647 " collide"<<std::endl;
1650 bool n2_is_source = !content_flowing_liquid(n2.d);
1651 u8 n2_liquid_level = 8;
1652 if(n2_is_source == false)
1653 n2_liquid_level = n2.param2 & 0x07;
1662 // Just flow into the source, nothing changes.
1663 // n2_changed is not set because destination didn't change
1668 if(liquid_next_level > liquid_level)
1670 n2.param2 = liquid_next_level;
1678 else if(n2.d == CONTENT_AIR)
1681 n2.param2 = liquid_next_level;
1688 //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
1692 m_transforming_liquid.push_back(p2);
1694 v3s16 blockpos = getNodeBlockPos(p2);
1695 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1697 modified_blocks.insert(blockpos, block);
1700 // If n2_changed to bottom, don't flow anywhere else
1701 if(to_bottom && flowed && !is_source)
1704 }catch(InvalidPositionException &e)
1710 //if(loopcount >= 100000)
1711 if(loopcount >= initial_size * 1)
1714 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1717 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1719 v3s16 blockpos = getNodeBlockPos(p);
1720 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1721 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1724 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1728 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1732 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1734 v3s16 blockpos = getNodeBlockPos(p);
1735 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1736 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1739 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1743 block->m_node_metadata.set(p_rel, meta);
1746 void Map::removeNodeMetadata(v3s16 p)
1748 v3s16 blockpos = getNodeBlockPos(p);
1749 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1750 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1753 dstream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1757 block->m_node_metadata.remove(p_rel);
1760 void Map::nodeMetadataStep(float dtime,
1761 core::map<v3s16, MapBlock*> &changed_blocks)
1765 Currently there is no way to ensure that all the necessary
1766 blocks are loaded when this is run. (They might get unloaded)
1767 NOTE: ^- Actually, that might not be so. In a quick test it
1768 reloaded a block with a furnace when I walked back to it from
1771 core::map<v2s16, MapSector*>::Iterator si;
1772 si = m_sectors.getIterator();
1773 for(; si.atEnd() == false; si++)
1775 MapSector *sector = si.getNode()->getValue();
1776 core::list< MapBlock * > sectorblocks;
1777 sector->getBlocks(sectorblocks);
1778 core::list< MapBlock * >::Iterator i;
1779 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1781 MapBlock *block = *i;
1782 bool changed = block->m_node_metadata.step(dtime);
1784 changed_blocks[block->getPos()] = block;
1793 ServerMap::ServerMap(std::string savedir):
1796 m_map_metadata_changed(true)
1798 dstream<<__FUNCTION_NAME<<std::endl;
1801 //m_chunksize = 16; // Too slow
1802 m_chunksize = 8; // Takes a few seconds
1806 m_seed = (((u64)(myrand()%0xffff)<<0)
1807 + ((u64)(myrand()%0xffff)<<16)
1808 + ((u64)(myrand()%0xffff)<<32)
1809 + ((u64)(myrand()%0xffff)<<48));
1812 Experimental and debug stuff
1819 Try to load map; if not found, create a new one.
1822 m_savedir = savedir;
1823 m_map_saving_enabled = false;
1827 // If directory exists, check contents and load if possible
1828 if(fs::PathExists(m_savedir))
1830 // If directory is empty, it is safe to save into it.
1831 if(fs::GetDirListing(m_savedir).size() == 0)
1833 dstream<<DTIME<<"Server: Empty save directory is valid."
1835 m_map_saving_enabled = true;
1840 // Load map metadata (seed, chunksize)
1843 // Load chunk metadata
1846 catch(FileNotGoodException &e){
1847 dstream<<DTIME<<"WARNING: Server: Could not load "
1848 <<"metafile(s). Disabling chunk-based "
1849 <<"generation."<<std::endl;
1853 /*// Load sector (0,0) and throw and exception on fail
1854 if(loadSectorFull(v2s16(0,0)) == false)
1855 throw LoadError("Failed to load sector (0,0)");*/
1857 /*dstream<<DTIME<<"Server: Successfully loaded chunk "
1858 "metadata and sector (0,0) from "<<savedir<<
1859 ", assuming valid save directory."
1862 dstream<<DTIME<<"INFO: Server: Successfully loaded map "
1863 <<"and chunk metadata from "<<savedir
1864 <<", assuming valid save directory."
1867 m_map_saving_enabled = true;
1868 // Map loaded, not creating new one
1872 // If directory doesn't exist, it is safe to save to it
1874 m_map_saving_enabled = true;
1877 catch(std::exception &e)
1879 dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
1880 <<", exception: "<<e.what()<<std::endl;
1881 dstream<<"Please remove the map or fix it."<<std::endl;
1882 dstream<<"WARNING: Map saving will be disabled."<<std::endl;
1885 dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
1887 // Create zero sector
1888 emergeSector(v2s16(0,0));
1890 // Initially write whole map
1894 ServerMap::~ServerMap()
1896 dstream<<__FUNCTION_NAME<<std::endl;
1900 if(m_map_saving_enabled)
1903 // Save only changed parts
1905 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1909 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1912 catch(std::exception &e)
1914 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1915 <<", exception: "<<e.what()<<std::endl;
1921 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1922 for(; i.atEnd() == false; i++)
1924 MapChunk *chunk = i.getNode()->getValue();
1930 Some helper functions for the map generator
1933 s16 find_ground_level(VoxelManipulator &vmanip, v2s16 p2d)
1935 v3s16 em = vmanip.m_area.getExtent();
1936 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
1937 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
1938 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
1940 for(y=y_nodes_max; y>=y_nodes_min; y--)
1942 MapNode &n = vmanip.m_data[i];
1943 if(content_walkable(n.d))
1946 vmanip.m_area.add_y(em, i, -1);
1948 if(y >= y_nodes_min)
1954 s16 find_ground_level_clever(VoxelManipulator &vmanip, v2s16 p2d)
1956 v3s16 em = vmanip.m_area.getExtent();
1957 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
1958 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
1959 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
1961 for(y=y_nodes_max; y>=y_nodes_min; y--)
1963 MapNode &n = vmanip.m_data[i];
1964 if(content_walkable(n.d)
1965 && n.d != CONTENT_TREE
1966 && n.d != CONTENT_LEAVES)
1969 vmanip.m_area.add_y(em, i, -1);
1971 if(y >= y_nodes_min)
1977 void make_tree(VoxelManipulator &vmanip, v3s16 p0)
1979 MapNode treenode(CONTENT_TREE);
1980 MapNode leavesnode(CONTENT_LEAVES);
1982 s16 trunk_h = myrand_range(3, 6);
1984 for(s16 ii=0; ii<trunk_h; ii++)
1986 if(vmanip.m_area.contains(p1))
1987 vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
1991 // p1 is now the last piece of the trunk
1994 VoxelArea leaves_a(v3s16(-2,-2,-2), v3s16(2,2,2));
1995 //SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
1996 Buffer<u8> leaves_d(leaves_a.getVolume());
1997 for(s32 i=0; i<leaves_a.getVolume(); i++)
2000 // Force leaves at near the end of the trunk
2003 for(s16 z=-d; z<=d; z++)
2004 for(s16 y=-d; y<=d; y++)
2005 for(s16 x=-d; x<=d; x++)
2007 leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
2011 // Add leaves randomly
2012 for(u32 iii=0; iii<7; iii++)
2017 myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
2018 myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
2019 myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
2022 for(s16 z=0; z<=d; z++)
2023 for(s16 y=0; y<=d; y++)
2024 for(s16 x=0; x<=d; x++)
2026 leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
2030 // Blit leaves to vmanip
2031 for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
2032 for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
2033 for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
2037 if(vmanip.m_area.contains(p) == false)
2039 u32 vi = vmanip.m_area.index(p);
2040 if(vmanip.m_data[vi].d != CONTENT_AIR)
2042 u32 i = leaves_a.index(x,y,z);
2043 if(leaves_d[i] == 1)
2044 vmanip.m_data[vi] = leavesnode;
2048 void make_papyrus(VoxelManipulator &vmanip, v3s16 p0)
2050 MapNode papyrusnode(CONTENT_PAPYRUS);
2052 s16 trunk_h = myrand_range(2, 3);
2054 for(s16 ii=0; ii<trunk_h; ii++)
2056 if(vmanip.m_area.contains(p1))
2057 vmanip.m_data[vmanip.m_area.index(p1)] = papyrusnode;
2062 void make_cactus(VoxelManipulator &vmanip, v3s16 p0)
2064 MapNode cactusnode(CONTENT_CACTUS);
2068 for(s16 ii=0; ii<trunk_h; ii++)
2070 if(vmanip.m_area.contains(p1))
2071 vmanip.m_data[vmanip.m_area.index(p1)] = cactusnode;
2077 Noise functions. Make sure seed is mangled differently in each one.
2080 // Amount of trees per area in nodes
2081 double tree_amount_2d(u64 seed, v2s16 p)
2083 double noise = noise2d_perlin(
2084 0.5+(float)p.X/250, 0.5+(float)p.Y/250,
2086 double zeroval = -0.3;
2090 return 0.04 * (noise-zeroval) / (1.0-zeroval);
2093 #define AVERAGE_MUD_AMOUNT 4
2095 double base_rock_level_2d(u64 seed, v2s16 p)
2097 // The base ground level
2098 double base = (double)WATER_LEVEL - (double)AVERAGE_MUD_AMOUNT
2099 + 20. * noise2d_perlin(
2100 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2101 (seed>>32)+654879876, 6, 0.6);
2103 /*// A bit hillier one
2104 double base2 = WATER_LEVEL - 4.0 + 40. * noise2d_perlin(
2105 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2106 (seed>>27)+90340, 6, 0.69);
2110 // Higher ground level
2111 double higher = (double)WATER_LEVEL + 25. + 35. * noise2d_perlin(
2112 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2113 seed+85039, 5, 0.69);
2114 //higher = 30; // For debugging
2116 // Limit higher to at least base
2120 // Steepness factor of cliffs
2121 double b = 1.0 + 1.0 * noise2d_perlin(
2122 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2124 b = rangelim(b, 0.0, 1000.0);
2127 b = rangelim(b, 3.0, 1000.0);
2128 //dstream<<"b="<<b<<std::endl;
2131 // Offset to more low
2132 double a_off = -0.2;
2133 // High/low selector
2134 /*double a = 0.5 + b * (a_off + noise2d_perlin(
2135 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2136 seed-359, 6, 0.7));*/
2137 double a = (double)0.5 + b * (a_off + noise2d_perlin(
2138 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2139 seed-359, 5, 0.60));
2141 a = rangelim(a, 0.0, 1.0);
2143 //dstream<<"a="<<a<<std::endl;
2145 double h = base*(1.0-a) + higher*a;
2152 double get_mud_add_amount(u64 seed, v2s16 p)
2154 return ((float)AVERAGE_MUD_AMOUNT + 3.0 * noise2d_perlin(
2155 0.5+(float)p.X/200, 0.5+(float)p.Y/200,
2156 seed+91013, 3, 0.55));
2160 Adds random objects to block, depending on the content of the block
2162 void addRandomObjects(MapBlock *block)
2164 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2165 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2167 bool last_node_walkable = false;
2168 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2171 MapNode n = block->getNodeNoEx(p);
2172 if(n.d == CONTENT_IGNORE)
2174 if(content_features(n.d).liquid_type != LIQUID_NONE)
2176 if(content_features(n.d).walkable)
2178 last_node_walkable = true;
2181 if(last_node_walkable)
2183 // If block contains light information
2184 if(content_features(n.d).param_type == CPT_LIGHT)
2186 if(n.getLight(LIGHTBANK_DAY) <= 3)
2188 if(myrand() % 300 == 0)
2190 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
2192 ServerActiveObject *obj = new RatSAO(NULL, 0, pos_f);
2193 std::string data = obj->getStaticData();
2194 StaticObject s_obj(obj->getType(),
2195 obj->getBasePosition(), data);
2197 block->m_static_objects.insert(0, s_obj);
2198 block->m_static_objects.insert(0, s_obj);
2199 block->m_static_objects.insert(0, s_obj);
2200 block->m_static_objects.insert(0, s_obj);
2201 block->m_static_objects.insert(0, s_obj);
2202 block->m_static_objects.insert(0, s_obj);
2205 if(myrand() % 300 == 0)
2207 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
2209 ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f);
2210 std::string data = obj->getStaticData();
2211 StaticObject s_obj(obj->getType(),
2212 obj->getBasePosition(), data);
2214 block->m_static_objects.insert(0, s_obj);
2220 last_node_walkable = false;
2223 block->setChangedFlag();
2226 #define VMANIP_FLAG_DUNGEON VOXELFLAG_CHECKED1
2229 This is the main map generation method
2232 void makeChunk(ChunkMakeData *data)
2237 s16 y_nodes_min = data->y_blocks_min * MAP_BLOCKSIZE;
2238 s16 y_nodes_max = data->y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
2239 s16 h_blocks = data->y_blocks_max - data->y_blocks_min + 1;
2240 u32 relative_volume = (u32)data->sectorpos_base_size*MAP_BLOCKSIZE
2241 *(u32)data->sectorpos_base_size*MAP_BLOCKSIZE
2242 *(u32)h_blocks*MAP_BLOCKSIZE;
2243 v3s16 bigarea_blocks_min(
2244 data->sectorpos_bigbase.X,
2246 data->sectorpos_bigbase.Y
2248 v3s16 bigarea_blocks_max(
2249 data->sectorpos_bigbase.X + data->sectorpos_bigbase_size - 1,
2251 data->sectorpos_bigbase.Y + data->sectorpos_bigbase_size - 1
2253 s16 lighting_min_d = 0-data->max_spread_amount;
2254 s16 lighting_max_d = data->sectorpos_base_size*MAP_BLOCKSIZE
2255 + data->max_spread_amount-1;
2258 data->vmanip.clearFlag(0xff);
2260 TimeTaker timer_generate("makeChunk() generate");
2262 // Maximum height of the stone surface and obstacles.
2263 // This is used to disable cave generation from going too high.
2264 s16 stone_surface_max_y = 0;
2267 Generate general ground level to full area
2271 TimeTaker timer1("Generating ground level");
2274 NoiseBuffer noisebuf1;
2275 //NoiseBuffer noisebuf2;
2278 data->sectorpos_bigbase.X*MAP_BLOCKSIZE,
2280 data->sectorpos_bigbase.Y*MAP_BLOCKSIZE
2282 v3f maxpos_f = minpos_f + v3f(
2283 data->sectorpos_bigbase_size*MAP_BLOCKSIZE,
2284 y_nodes_max-y_nodes_min,
2285 data->sectorpos_bigbase_size*MAP_BLOCKSIZE
2287 v3f samplelength_f = v3f(4.0, 4.0, 4.0);
2289 TimeTaker timer("noisebuf.create");
2291 noisebuf1.create(data->seed+25104, 6, 0.60, 200.0,
2292 minpos_f.X, minpos_f.Y, minpos_f.Z,
2293 maxpos_f.X, maxpos_f.Y, maxpos_f.Z,
2294 samplelength_f.X, samplelength_f.Y, samplelength_f.Z);
2295 /*noisebuf1.create(data->seed+25104, 3, 0.60, 25.0,
2296 minpos_f.X, minpos_f.Y, minpos_f.Z,
2297 maxpos_f.X, maxpos_f.Y, maxpos_f.Z,
2298 samplelength_f.X, samplelength_f.Y, samplelength_f.Z);
2299 noisebuf2.create(data->seed+25105, 4, 0.50, 200.0,
2300 minpos_f.X, minpos_f.Y, minpos_f.Z,
2301 maxpos_f.X, maxpos_f.Y, maxpos_f.Z,
2302 samplelength_f.X, samplelength_f.Y, samplelength_f.Z);*/
2305 for(s16 x=0; x<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2306 for(s16 z=0; z<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2309 v2s16 p2d = data->sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2311 // Ground height at this point
2312 float surface_y_f = 0.0;
2314 // Use perlin noise for ground height
2315 surface_y_f = base_rock_level_2d(data->seed, p2d);
2316 //surface_y_f = base_rock_level_2d(data->seed, p2d);
2318 // Convert to integer
2319 s16 surface_y = (s16)surface_y_f;
2322 if(surface_y > stone_surface_max_y)
2323 stone_surface_max_y = surface_y;
2326 Fill ground with stone
2329 // Use fast index incrementing
2330 v3s16 em = data->vmanip.m_area.getExtent();
2331 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2332 for(s16 y=y_nodes_min; y<=y_nodes_max; y++)
2334 // Skip if already generated.
2335 // This is done here because there might be a cave at
2336 // any point in ground, which could look like it
2337 // wasn't generated.
2338 if(data->vmanip.m_data[i].d != CONTENT_AIR)
2341 /*s16 noiseval = 50.0 * noise3d_perlin(
2342 0.5+(float)p2d.X/100.0,
2344 0.5+(float)p2d.Y/100.0,
2345 data->seed+123, 5, 0.5);*/
2346 double noiseval = 64.0 * noisebuf1.get(p2d.X, y, p2d.Y);
2347 /*double noiseval = 30.0 * noisebuf1.get(p2d.X, y, p2d.Y);
2348 noiseval *= MYMAX(0, -0.2 + noisebuf2.get(p2d.X, y, p2d.Y));*/
2350 //if(y < surface_y + noiseval)
2353 data->vmanip.m_data[i].d = CONTENT_STONE;
2355 data->vmanip.m_area.add_y(em, i, 1);
2362 for(s16 x=0; x<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2363 for(s16 z=0; z<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2366 v2s16 p2d = data->sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2369 Skip of already generated
2372 v3s16 p(p2d.X, y_nodes_min, p2d.Y);
2373 if(data->vmanip.m_data[data->vmanip.m_area.index(p)].d != CONTENT_AIR)
2377 // Ground height at this point
2378 float surface_y_f = 0.0;
2380 // Use perlin noise for ground height
2381 surface_y_f = base_rock_level_2d(data->seed, p2d);
2383 /*// Experimental stuff
2385 float a = highlands_level_2d(data->seed, p2d);
2390 // Convert to integer
2391 s16 surface_y = (s16)surface_y_f;
2394 if(surface_y > stone_surface_max_y)
2395 stone_surface_max_y = surface_y;
2398 Fill ground with stone
2401 // Use fast index incrementing
2402 v3s16 em = data->vmanip.m_area.getExtent();
2403 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2404 for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++)
2406 // Skip if already generated.
2407 // This is done here because there might be a cave at
2408 // any point in ground, which could look like it
2409 // wasn't generated.
2410 if(data->vmanip.m_data[i].d != CONTENT_AIR)
2413 data->vmanip.m_data[i].d = CONTENT_STONE;
2415 data->vmanip.m_area.add_y(em, i, 1);
2424 Randomize some parameters
2427 //s32 stone_obstacle_count = 0;
2428 /*s32 stone_obstacle_count =
2429 rangelim((1.0+noise2d(data->seed+897,
2430 data->sectorpos_base.X, data->sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2432 //s16 stone_obstacle_max_height = 0;
2433 /*s16 stone_obstacle_max_height =
2434 rangelim((1.0+noise2d(data->seed+5902,
2435 data->sectorpos_base.X, data->sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2438 Loop this part, it will make stuff look older and newer nicely
2440 const u32 age_loops = 2;
2441 for(u32 i_age=0; i_age<age_loops; i_age++)
2443 /******************************
2444 BEGINNING OF AGING LOOP
2445 ******************************/
2450 //TimeTaker timer1("caves");
2455 u32 caves_count = relative_volume / 400000;
2456 u32 bruises_count = relative_volume * stone_surface_max_y / 40000000;
2457 if(stone_surface_max_y < WATER_LEVEL)
2459 /*u32 caves_count = 0;
2460 u32 bruises_count = 0;*/
2461 for(u32 jj=0; jj<caves_count+bruises_count; jj++)
2463 s16 min_tunnel_diameter = 3;
2464 s16 max_tunnel_diameter = 5;
2465 u16 tunnel_routepoints = 20;
2467 v3f main_direction(0,0,0);
2469 bool bruise_surface = (jj > caves_count);
2473 min_tunnel_diameter = 5;
2474 max_tunnel_diameter = myrand_range(10, 20);
2475 /*min_tunnel_diameter = MYMAX(0, stone_surface_max_y/6);
2476 max_tunnel_diameter = myrand_range(MYMAX(0, stone_surface_max_y/6), MYMAX(0, stone_surface_max_y/2));*/
2478 /*s16 tunnel_rou = rangelim(25*(0.5+1.0*noise2d(data->seed+42,
2479 data->sectorpos_base.X, data->sectorpos_base.Y)), 0, 15);*/
2481 tunnel_routepoints = 5;
2487 // Allowed route area size in nodes
2489 data->sectorpos_base_size*MAP_BLOCKSIZE,
2490 h_blocks*MAP_BLOCKSIZE,
2491 data->sectorpos_base_size*MAP_BLOCKSIZE
2494 // Area starting point in nodes
2496 data->sectorpos_base.X*MAP_BLOCKSIZE,
2497 data->y_blocks_min*MAP_BLOCKSIZE,
2498 data->sectorpos_base.Y*MAP_BLOCKSIZE
2502 //(this should be more than the maximum radius of the tunnel)
2503 //s16 insure = 5; // Didn't work with max_d = 20
2505 s16 more = data->max_spread_amount - max_tunnel_diameter/2 - insure;
2506 ar += v3s16(1,0,1) * more * 2;
2507 of -= v3s16(1,0,1) * more;
2509 s16 route_y_min = 0;
2510 // Allow half a diameter + 7 over stone surface
2511 s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2 + 7;
2513 /*// If caves, don't go through surface too often
2514 if(bruise_surface == false)
2515 route_y_max -= myrand_range(0, max_tunnel_diameter*2);*/
2517 // Limit maximum to area
2518 route_y_max = rangelim(route_y_max, 0, ar.Y-1);
2522 /*// Minimum is at y=0
2523 route_y_min = -of.Y - 0;*/
2524 // Minimum is at y=max_tunnel_diameter/4
2525 //route_y_min = -of.Y + max_tunnel_diameter/4;
2526 //s16 min = -of.Y + max_tunnel_diameter/4;
2527 s16 min = -of.Y + 0;
2528 route_y_min = myrand_range(min, min + max_tunnel_diameter);
2529 route_y_min = rangelim(route_y_min, 0, route_y_max);
2532 /*dstream<<"route_y_min = "<<route_y_min
2533 <<", route_y_max = "<<route_y_max<<std::endl;*/
2535 s16 route_start_y_min = route_y_min;
2536 s16 route_start_y_max = route_y_max;
2538 // Start every 2nd cave from surface
2539 bool coming_from_surface = (jj % 2 == 0 && bruise_surface == false);
2541 if(coming_from_surface)
2543 route_start_y_min = -of.Y + stone_surface_max_y + 10;
2546 route_start_y_min = rangelim(route_start_y_min, 0, ar.Y-1);
2547 route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y-1);
2549 // Randomize starting position
2551 (float)(myrand()%ar.X)+0.5,
2552 (float)(myrand_range(route_start_y_min, route_start_y_max))+0.5,
2553 (float)(myrand()%ar.Z)+0.5
2556 MapNode airnode(CONTENT_AIR);
2559 Generate some tunnel starting from orp
2562 for(u16 j=0; j<tunnel_routepoints; j++)
2564 if(j%7==0 && bruise_surface == false)
2566 main_direction = v3f(
2567 ((float)(myrand()%20)-(float)10)/10,
2568 ((float)(myrand()%20)-(float)10)/30,
2569 ((float)(myrand()%20)-(float)10)/10
2571 main_direction *= (float)myrand_range(1, 3);
2575 s16 min_d = min_tunnel_diameter;
2576 s16 max_d = max_tunnel_diameter;
2577 s16 rs = myrand_range(min_d, max_d);
2582 maxlen = v3s16(rs*7,rs*7,rs*7);
2586 maxlen = v3s16(rs*4, myrand_range(1, rs*3), rs*4);
2591 if(coming_from_surface && j < 3)
2594 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2595 (float)(myrand()%(maxlen.Y*1))-(float)maxlen.Y,
2596 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2602 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2603 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2604 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2608 vec += main_direction;
2613 else if(rp.X >= ar.X)
2615 if(rp.Y < route_y_min)
2617 else if(rp.Y >= route_y_max)
2618 rp.Y = route_y_max-1;
2621 else if(rp.Z >= ar.Z)
2625 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2627 v3f fp = orp + vec * f;
2628 v3s16 cp(fp.X, fp.Y, fp.Z);
2631 s16 d1 = d0 + rs - 1;
2632 for(s16 z0=d0; z0<=d1; z0++)
2634 //s16 si = rs - MYMAX(0, abs(z0)-rs/4);
2635 s16 si = rs - MYMAX(0, abs(z0)-rs/7);
2636 for(s16 x0=-si; x0<=si-1; x0++)
2638 s16 maxabsxz = MYMAX(abs(x0), abs(z0));
2639 //s16 si2 = rs - MYMAX(0, maxabsxz-rs/4);
2640 s16 si2 = rs - MYMAX(0, maxabsxz-rs/7);
2641 //s16 si2 = rs - abs(x0);
2642 for(s16 y0=-si2+1+2; y0<=si2-1; y0++)
2648 /*if(isInArea(p, ar) == false)
2650 // Check only height
2651 if(y < 0 || y >= ar.Y)
2655 //assert(data->vmanip.m_area.contains(p));
2656 if(data->vmanip.m_area.contains(p) == false)
2658 dstream<<"WARNING: "<<__FUNCTION_NAME
2659 <<":"<<__LINE__<<": "
2660 <<"point not in area"
2665 // Just set it to air, it will be changed to
2667 u32 i = data->vmanip.m_area.index(p);
2668 data->vmanip.m_data[i] = airnode;
2670 if(bruise_surface == false)
2673 data->vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON;
2691 //TimeTaker timer1("ore veins");
2696 for(u32 jj=0; jj<relative_volume/1000; jj++)
2698 s16 max_vein_diameter = 3;
2700 // Allowed route area size in nodes
2702 data->sectorpos_base_size*MAP_BLOCKSIZE,
2703 h_blocks*MAP_BLOCKSIZE,
2704 data->sectorpos_base_size*MAP_BLOCKSIZE
2707 // Area starting point in nodes
2709 data->sectorpos_base.X*MAP_BLOCKSIZE,
2710 data->y_blocks_min*MAP_BLOCKSIZE,
2711 data->sectorpos_base.Y*MAP_BLOCKSIZE
2715 //(this should be more than the maximum radius of the tunnel)
2717 s16 more = data->max_spread_amount - max_vein_diameter/2 - insure;
2718 ar += v3s16(1,0,1) * more * 2;
2719 of -= v3s16(1,0,1) * more;
2721 // Randomize starting position
2723 (float)(myrand()%ar.X)+0.5,
2724 (float)(myrand()%ar.Y)+0.5,
2725 (float)(myrand()%ar.Z)+0.5
2728 // Randomize mineral
2731 mineral = MINERAL_COAL;
2733 mineral = MINERAL_IRON;
2736 Generate some vein starting from orp
2739 for(u16 j=0; j<2; j++)
2742 (float)(myrand()%ar.X)+0.5,
2743 (float)(myrand()%ar.Y)+0.5,
2744 (float)(myrand()%ar.Z)+0.5
2746 v3f vec = rp - orp;*/
2748 v3s16 maxlen(5, 5, 5);
2750 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2751 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2752 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2757 else if(rp.X >= ar.X)
2761 else if(rp.Y >= ar.Y)
2765 else if(rp.Z >= ar.Z)
2771 s16 max_d = max_vein_diameter;
2772 s16 rs = myrand_range(min_d, max_d);
2774 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2776 v3f fp = orp + vec * f;
2777 v3s16 cp(fp.X, fp.Y, fp.Z);
2779 s16 d1 = d0 + rs - 1;
2780 for(s16 z0=d0; z0<=d1; z0++)
2782 s16 si = rs - abs(z0);
2783 for(s16 x0=-si; x0<=si-1; x0++)
2785 s16 si2 = rs - abs(x0);
2786 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2788 // Don't put mineral to every place
2796 /*if(isInArea(p, ar) == false)
2798 // Check only height
2799 if(y < 0 || y >= ar.Y)
2803 assert(data->vmanip.m_area.contains(p));
2805 // Just set it to air, it will be changed to
2807 u32 i = data->vmanip.m_area.index(p);
2808 MapNode *n = &data->vmanip.m_data[i];
2809 if(n->d == CONTENT_STONE)
2827 TimeTaker timer1("add mud");
2830 Add mud to the central chunk
2833 for(s16 x=0; x<data->sectorpos_base_size*MAP_BLOCKSIZE; x++)
2834 for(s16 z=0; z<data->sectorpos_base_size*MAP_BLOCKSIZE; z++)
2836 // Node position in 2d
2837 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2839 // Randomize mud amount
2840 s16 mud_add_amount = get_mud_add_amount(data->seed, p2d) / 2.0;
2842 // Find ground level
2843 s16 surface_y = find_ground_level_clever(data->vmanip, p2d);
2846 If topmost node is grass, change it to mud.
2847 It might be if it was flown to there from a neighboring
2848 chunk and then converted.
2851 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y));
2852 MapNode *n = &data->vmanip.m_data[i];
2853 if(n->d == CONTENT_GRASS)
2854 *n = MapNode(CONTENT_MUD);
2855 //n->d = CONTENT_MUD;
2863 v3s16 em = data->vmanip.m_area.getExtent();
2864 s16 y_start = surface_y+1;
2865 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2866 for(s16 y=y_start; y<=y_nodes_max; y++)
2868 if(mudcount >= mud_add_amount)
2871 MapNode &n = data->vmanip.m_data[i];
2872 n = MapNode(CONTENT_MUD);
2873 //n.d = CONTENT_MUD;
2876 data->vmanip.m_area.add_y(em, i, 1);
2888 TimeTaker timer1("flow mud");
2891 Flow mud away from steep edges
2894 // Limit area by 1 because mud is flown into neighbors.
2895 s16 mudflow_minpos = 0-data->max_spread_amount+1;
2896 s16 mudflow_maxpos = data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-2;
2898 // Iterate a few times
2899 for(s16 k=0; k<3; k++)
2902 for(s16 x=mudflow_minpos;
2905 for(s16 z=mudflow_minpos;
2909 // Invert coordinates every 2nd iteration
2912 x = mudflow_maxpos - (x-mudflow_minpos);
2913 z = mudflow_maxpos - (z-mudflow_minpos);
2916 // Node position in 2d
2917 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2919 v3s16 em = data->vmanip.m_area.getExtent();
2920 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2927 for(; y>=y_nodes_min; y--)
2929 n = &data->vmanip.m_data[i];
2930 //if(content_walkable(n->d))
2932 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2935 data->vmanip.m_area.add_y(em, i, -1);
2938 // Stop if out of area
2939 //if(data->vmanip.m_area.contains(i) == false)
2943 /*// If not mud, do nothing to it
2944 MapNode *n = &data->vmanip.m_data[i];
2945 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
2949 Don't flow it if the stuff under it is not mud
2953 data->vmanip.m_area.add_y(em, i2, -1);
2954 // Cancel if out of area
2955 if(data->vmanip.m_area.contains(i2) == false)
2957 MapNode *n2 = &data->vmanip.m_data[i2];
2958 if(n2->d != CONTENT_MUD && n2->d != CONTENT_GRASS)
2962 // Make it exactly mud
2965 /*s16 recurse_count = 0;
2969 v3s16(0,0,1), // back
2970 v3s16(1,0,0), // right
2971 v3s16(0,0,-1), // front
2972 v3s16(-1,0,0), // left
2975 // Theck that upper is air or doesn't exist.
2976 // Cancel dropping if upper keeps it in place
2978 data->vmanip.m_area.add_y(em, i3, 1);
2979 if(data->vmanip.m_area.contains(i3) == true
2980 && content_walkable(data->vmanip.m_data[i3].d) == true)
2987 for(u32 di=0; di<4; di++)
2989 v3s16 dirp = dirs4[di];
2992 data->vmanip.m_area.add_p(em, i2, dirp);
2993 // Fail if out of area
2994 if(data->vmanip.m_area.contains(i2) == false)
2996 // Check that side is air
2997 MapNode *n2 = &data->vmanip.m_data[i2];
2998 if(content_walkable(n2->d))
3000 // Check that under side is air
3001 data->vmanip.m_area.add_y(em, i2, -1);
3002 if(data->vmanip.m_area.contains(i2) == false)
3004 n2 = &data->vmanip.m_data[i2];
3005 if(content_walkable(n2->d))
3007 /*// Check that under that is air (need a drop of 2)
3008 data->vmanip.m_area.add_y(em, i2, -1);
3009 if(data->vmanip.m_area.contains(i2) == false)
3011 n2 = &data->vmanip.m_data[i2];
3012 if(content_walkable(n2->d))
3014 // Loop further down until not air
3016 data->vmanip.m_area.add_y(em, i2, -1);
3017 // Fail if out of area
3018 if(data->vmanip.m_area.contains(i2) == false)
3020 n2 = &data->vmanip.m_data[i2];
3021 }while(content_walkable(n2->d) == false);
3022 // Loop one up so that we're in air
3023 data->vmanip.m_area.add_y(em, i2, 1);
3024 n2 = &data->vmanip.m_data[i2];
3026 // Move mud to new place
3028 // Set old place to be air
3029 *n = MapNode(CONTENT_AIR);
3045 TimeTaker timer1("add water");
3048 Add water to the central chunk (and a bit more)
3051 for(s16 x=0-data->max_spread_amount;
3052 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3054 for(s16 z=0-data->max_spread_amount;
3055 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3058 // Node position in 2d
3059 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3061 // Find ground level
3062 //s16 surface_y = find_ground_level(data->vmanip, p2d);
3065 If ground level is over water level, skip.
3066 NOTE: This leaves caves near water without water,
3067 which looks especially crappy when the nearby water
3068 won't start flowing either for some reason
3070 /*if(surface_y > WATER_LEVEL)
3077 v3s16 em = data->vmanip.m_area.getExtent();
3078 u8 light = LIGHT_MAX;
3079 // Start at global water surface level
3080 s16 y_start = WATER_LEVEL;
3081 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3082 MapNode *n = &data->vmanip.m_data[i];
3084 for(s16 y=y_start; y>=y_nodes_min; y--)
3086 n = &data->vmanip.m_data[i];
3088 // Stop when there is no water and no air
3089 if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
3090 && n->d != CONTENT_WATER)
3096 // Make water only not in caves
3097 if(!(data->vmanip.m_flags[i]&VMANIP_FLAG_DUNGEON))
3099 n->d = CONTENT_WATERSOURCE;
3100 //n->setLight(LIGHTBANK_DAY, light);
3102 // Add to transforming liquid queue (in case it'd
3104 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3105 data->transforming_liquid.push_back(p);
3109 data->vmanip.m_area.add_y(em, i, -1);
3121 /***********************
3123 ************************/
3127 //TimeTaker timer1("convert mud to sand");
3133 //s16 mud_add_amount = myrand_range(2, 4);
3134 //s16 mud_add_amount = 0;
3136 /*for(s16 x=0; x<data->sectorpos_base_size*MAP_BLOCKSIZE; x++)
3137 for(s16 z=0; z<data->sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3138 for(s16 x=0-data->max_spread_amount+1;
3139 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3141 for(s16 z=0-data->max_spread_amount+1;
3142 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3145 // Node position in 2d
3146 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3148 // Determine whether to have sand here
3149 double sandnoise = noise2d_perlin(
3150 0.5+(float)p2d.X/500, 0.5+(float)p2d.Y/500,
3151 data->seed+59420, 3, 0.50);
3153 bool have_sand = (sandnoise > -0.15);
3155 if(have_sand == false)
3158 // Determine whether to have clay in the sand here
3159 double claynoise = noise2d_perlin(
3160 0.5+(float)p2d.X/500, 0.5+(float)p2d.Y/500,
3161 data->seed+4321, 6, 0.95);
3163 bool have_clay = have_sand && (claynoise > 1.25);
3165 // Find ground level
3166 s16 surface_y = find_ground_level_clever(data->vmanip, p2d);
3168 if(surface_y > WATER_LEVEL + 2)
3172 v3s16 em = data->vmanip.m_area.getExtent();
3173 s16 y_start = surface_y;
3174 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3175 u32 not_sand_counter = 0;
3176 for(s16 y=y_start; y>=y_nodes_min; y--)
3178 MapNode *n = &data->vmanip.m_data[i];
3179 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
3181 if(have_clay && (surface_y == WATER_LEVEL))
3182 n->d = CONTENT_CLAY;
3184 n->d = CONTENT_SAND;
3189 if(not_sand_counter > 3)
3193 data->vmanip.m_area.add_y(em, i, -1);
3205 //TimeTaker timer1("generate trees");
3211 // Divide area into parts
3213 s16 sidelen = data->sectorpos_base_size*MAP_BLOCKSIZE / div;
3214 double area = sidelen * sidelen;
3215 for(s16 x0=0; x0<div; x0++)
3216 for(s16 z0=0; z0<div; z0++)
3218 // Center position of part of division
3220 data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen/2 + sidelen*x0,
3221 data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen/2 + sidelen*z0
3223 // Minimum edge of part of division
3225 data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen*x0,
3226 data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen*z0
3228 // Maximum edge of part of division
3230 data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen + sidelen*x0 - 1,
3231 data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen + sidelen*z0 - 1
3234 u32 tree_count = area * tree_amount_2d(data->seed, p2d_center);
3235 // Put trees in random places on part of division
3236 for(u32 i=0; i<tree_count; i++)
3238 s16 x = myrand_range(p2d_min.X, p2d_max.X);
3239 s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
3240 s16 y = find_ground_level(data->vmanip, v2s16(x,z));
3241 // Don't make a tree under water level
3242 if(y < WATER_LEVEL - 1)
3244 // Don't make a tree so high that it doesn't fit
3245 if(y > y_nodes_max - 6)
3249 u32 i = data->vmanip.m_area.index(v3s16(p));
3250 MapNode *n = &data->vmanip.m_data[i];
3251 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS && n->d != CONTENT_SAND)
3253 // Papyrus grows only on mud and in water
3254 if(n->d == CONTENT_MUD && y == WATER_LEVEL - 1)
3257 make_papyrus(data->vmanip, p);
3259 // Don't make a tree under water level
3262 // Trees grow only on mud and grass
3263 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
3266 make_tree(data->vmanip, p);
3268 // Cactii grow only on sand
3269 else if(n->d == CONTENT_SAND)
3272 make_cactus(data->vmanip, p);
3277 /*u32 tree_max = relative_area / 60;
3278 //u32 count = myrand_range(0, tree_max);
3279 for(u32 i=0; i<count; i++)
3281 s16 x = myrand_range(0, data->sectorpos_base_size*MAP_BLOCKSIZE-1);
3282 s16 z = myrand_range(0, data->sectorpos_base_size*MAP_BLOCKSIZE-1);
3283 x += data->sectorpos_base.X*MAP_BLOCKSIZE;
3284 z += data->sectorpos_base.Y*MAP_BLOCKSIZE;
3285 s16 y = find_ground_level(data->vmanip, v2s16(x,z));
3286 // Don't make a tree under water level
3291 make_tree(data->vmanip, p);
3301 //TimeTaker timer1("grow grass");
3307 /*for(s16 x=0-4; x<data->sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
3308 for(s16 z=0-4; z<data->sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
3309 for(s16 x=0-data->max_spread_amount;
3310 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3312 for(s16 z=0-data->max_spread_amount;
3313 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3316 // Node position in 2d
3317 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3320 Find the lowest surface to which enough light ends up
3323 Basically just wait until not air and not leaves.
3327 v3s16 em = data->vmanip.m_area.getExtent();
3328 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
3330 // Go to ground level
3331 for(y=y_nodes_max; y>=y_nodes_min; y--)
3333 MapNode &n = data->vmanip.m_data[i];
3334 if(n.d != CONTENT_AIR
3335 && n.d != CONTENT_LEAVES)
3337 data->vmanip.m_area.add_y(em, i, -1);
3339 if(y >= y_nodes_min)
3342 surface_y = y_nodes_min;
3345 u32 i = data->vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
3346 MapNode *n = &data->vmanip.m_data[i];
3347 if(n->d == CONTENT_MUD)
3348 n->d = CONTENT_GRASS;
3355 Initial lighting (sunlight)
3358 core::map<v3s16, bool> light_sources;
3361 // 750ms @cs=8, can't optimize more
3362 TimeTaker timer1("initial lighting");
3364 // NOTE: This is no used... umm... for some reason!
3367 Go through the edges and add all nodes that have light to light_sources
3371 for(s16 i=0; i<4; i++)
3373 for(s16 j=lighting_min_d;
3380 if(i == 0 || i == 1)
3382 x = (i==0) ? lighting_min_d : lighting_max_d;
3391 z = (i==0) ? lighting_min_d : lighting_max_d;
3398 // Node position in 2d
3399 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3402 v3s16 em = data->vmanip.m_area.getExtent();
3403 s16 y_start = y_nodes_max;
3404 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3405 for(s16 y=y_start; y>=y_nodes_min; y--)
3407 MapNode *n = &data->vmanip.m_data[i];
3408 if(n->getLight(LIGHTBANK_DAY) != 0)
3410 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3412 //NOTE: This is broken, at least the index has to
3421 Go through the edges and apply sunlight to them, not caring
3426 for(s16 i=0; i<4; i++)
3428 for(s16 j=lighting_min_d;
3435 if(i == 0 || i == 1)
3437 x = (i==0) ? lighting_min_d : lighting_max_d;
3446 z = (i==0) ? lighting_min_d : lighting_max_d;
3453 // Node position in 2d
3454 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3456 // Loop from top to down
3458 u8 light = LIGHT_SUN;
3459 v3s16 em = data->vmanip.m_area.getExtent();
3460 s16 y_start = y_nodes_max;
3461 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3462 for(s16 y=y_start; y>=y_nodes_min; y--)
3464 MapNode *n = &data->vmanip.m_data[i];
3465 if(light_propagates_content(n->d) == false)
3469 else if(light != LIGHT_SUN
3470 || sunlight_propagates_content(n->d) == false)
3476 n->setLight(LIGHTBANK_DAY, light);
3477 n->setLight(LIGHTBANK_NIGHT, 0);
3481 // Insert light source
3482 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3485 // Increment index by y
3486 data->vmanip.m_area.add_y(em, i, -1);
3492 /*for(s16 x=0; x<data->sectorpos_base_size*MAP_BLOCKSIZE; x++)
3493 for(s16 z=0; z<data->sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3494 /*for(s16 x=0-data->max_spread_amount+1;
3495 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3497 for(s16 z=0-data->max_spread_amount+1;
3498 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3502 This has to be 1 smaller than the actual area, because
3503 neighboring nodes are checked.
3505 for(s16 x=lighting_min_d+1;
3506 x<=lighting_max_d-1;
3508 for(s16 z=lighting_min_d+1;
3509 z<=lighting_max_d-1;
3512 // Node position in 2d
3513 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3516 Apply initial sunlight
3519 u8 light = LIGHT_SUN;
3520 bool add_to_sources = false;
3521 v3s16 em = data->vmanip.m_area.getExtent();
3522 s16 y_start = y_nodes_max;
3523 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3524 for(s16 y=y_start; y>=y_nodes_min; y--)
3526 MapNode *n = &data->vmanip.m_data[i];
3528 if(light_propagates_content(n->d) == false)
3532 else if(light != LIGHT_SUN
3533 || sunlight_propagates_content(n->d) == false)
3539 // This doesn't take much time
3540 if(add_to_sources == false)
3543 Check sides. If side is not air or water, start
3544 adding to light_sources.
3547 v3s16(0,0,1), // back
3548 v3s16(1,0,0), // right
3549 v3s16(0,0,-1), // front
3550 v3s16(-1,0,0), // left
3552 for(u32 di=0; di<4; di++)
3554 v3s16 dirp = dirs4[di];
3556 data->vmanip.m_area.add_p(em, i2, dirp);
3557 MapNode *n2 = &data->vmanip.m_data[i2];
3559 n2->d != CONTENT_AIR
3560 && n2->d != CONTENT_WATERSOURCE
3561 && n2->d != CONTENT_WATER
3563 add_to_sources = true;
3569 n->setLight(LIGHTBANK_DAY, light);
3570 n->setLight(LIGHTBANK_NIGHT, 0);
3572 // This doesn't take much time
3573 if(light != 0 && add_to_sources)
3575 // Insert light source
3576 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3579 // Increment index by y
3580 data->vmanip.m_area.add_y(em, i, -1);
3588 // Spread light around
3590 TimeTaker timer("makeChunk() spreadLight");
3591 data->vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
3598 timer_generate.stop();
3601 //###################################################################
3602 //###################################################################
3603 //###################################################################
3604 //###################################################################
3605 //###################################################################
3606 //###################################################################
3607 //###################################################################
3608 //###################################################################
3609 //###################################################################
3610 //###################################################################
3611 //###################################################################
3612 //###################################################################
3613 //###################################################################
3614 //###################################################################
3615 //###################################################################
3617 void ServerMap::initChunkMake(ChunkMakeData &data, v2s16 chunkpos)
3619 if(m_chunksize == 0)
3627 // The distance how far into the neighbors the generator is allowed to go.
3628 s16 max_spread_amount_sectors = 2;
3629 assert(max_spread_amount_sectors <= m_chunksize);
3630 s16 max_spread_amount = max_spread_amount_sectors * MAP_BLOCKSIZE;
3632 s16 y_blocks_min = -4;
3633 s16 y_blocks_max = 3;
3635 v2s16 sectorpos_base = chunk_to_sector(chunkpos);
3636 s16 sectorpos_base_size = m_chunksize;
3638 v2s16 sectorpos_bigbase =
3639 sectorpos_base - v2s16(1,1) * max_spread_amount_sectors;
3640 s16 sectorpos_bigbase_size =
3641 sectorpos_base_size + 2 * max_spread_amount_sectors;
3644 const s16 limit = MAP_GENERATION_LIMIT / MAP_BLOCKSIZE;
3645 if(sectorpos_bigbase.X < -limit
3646 || sectorpos_bigbase.X + sectorpos_bigbase_size >= limit
3647 || sectorpos_bigbase.Y < -limit
3648 || sectorpos_bigbase.Y + sectorpos_bigbase_size >= limit)
3655 data.chunkpos = chunkpos;
3656 data.y_blocks_min = y_blocks_min;
3657 data.y_blocks_max = y_blocks_max;
3658 data.sectorpos_base = sectorpos_base;
3659 data.sectorpos_base_size = sectorpos_base_size;
3660 data.sectorpos_bigbase = sectorpos_bigbase;
3661 data.sectorpos_bigbase_size = sectorpos_bigbase_size;
3662 data.max_spread_amount = max_spread_amount;
3665 Create the whole area of this and the neighboring chunks
3668 TimeTaker timer("initChunkMake() create area");
3670 for(s16 x=0; x<sectorpos_bigbase_size; x++)
3671 for(s16 z=0; z<sectorpos_bigbase_size; z++)
3673 v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
3674 ServerMapSector *sector = createSector(sectorpos);
3677 for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
3679 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
3680 MapBlock *block = createBlock(blockpos);
3682 // Lighting won't be calculated
3683 //block->setLightingExpired(true);
3684 // Lighting will be calculated
3685 block->setLightingExpired(false);
3688 Block gets sunlight if this is true.
3690 This should be set to true when the top side of a block
3691 is completely exposed to the sky.
3693 Actually this doesn't matter now because the
3694 initial lighting is done here.
3696 block->setIsUnderground(y != y_blocks_max);
3702 Now we have a big empty area.
3704 Make a ManualMapVoxelManipulator that contains this and the
3708 v3s16 bigarea_blocks_min(
3709 sectorpos_bigbase.X,
3713 v3s16 bigarea_blocks_max(
3714 sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
3716 sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
3719 data.vmanip.setMap(this);
3722 TimeTaker timer("initChunkMake() initialEmerge");
3723 data.vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
3728 MapChunk* ServerMap::finishChunkMake(ChunkMakeData &data,
3729 core::map<v3s16, MapBlock*> &changed_blocks)
3735 Blit generated stuff to map
3739 //TimeTaker timer("generateChunkRaw() blitBackAll");
3740 data.vmanip.blitBackAll(&changed_blocks);
3744 Update day/night difference cache of the MapBlocks
3747 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
3748 i.atEnd() == false; i++)
3750 MapBlock *block = i.getNode()->getValue();
3751 block->updateDayNightDiff();
3756 Copy transforming liquid information
3758 while(data.transforming_liquid.size() > 0)
3760 v3s16 p = data.transforming_liquid.pop_front();
3761 m_transforming_liquid.push_back(p);
3765 Add random objects to blocks
3768 for(s16 x=0; x<data.sectorpos_base_size; x++)
3769 for(s16 z=0; z<data.sectorpos_base_size; z++)
3771 v2s16 sectorpos = data.sectorpos_base + v2s16(x,z);
3772 ServerMapSector *sector = createSector(sectorpos);
3775 for(s16 y=data.y_blocks_min; y<=data.y_blocks_max; y++)
3777 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
3778 MapBlock *block = createBlock(blockpos);
3779 addRandomObjects(block);
3785 Create chunk metadata
3788 for(s16 x=-1; x<=1; x++)
3789 for(s16 y=-1; y<=1; y++)
3791 v2s16 chunkpos0 = data.chunkpos + v2s16(x,y);
3792 // Add chunk meta information
3793 MapChunk *chunk = getChunk(chunkpos0);
3796 chunk = new MapChunk();
3797 m_chunks.insert(chunkpos0, chunk);
3799 //chunk->setIsVolatile(true);
3800 if(chunk->getGenLevel() > GENERATED_PARTLY)
3801 chunk->setGenLevel(GENERATED_PARTLY);
3805 Set central chunk non-volatile
3807 MapChunk *chunk = getChunk(data.chunkpos);
3810 //chunk->setIsVolatile(false);
3811 chunk->setGenLevel(GENERATED_FULLY);
3814 Save changed parts of map
3823 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
3824 core::map<v3s16, MapBlock*> &changed_blocks,
3827 DSTACK(__FUNCTION_NAME);
3830 Don't generate if already fully generated
3834 MapChunk *chunk = getChunk(chunkpos);
3835 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3837 dstream<<"generateChunkRaw(): Chunk "
3838 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
3839 <<" already generated"<<std::endl;
3844 dstream<<"generateChunkRaw(): Generating chunk "
3845 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
3848 TimeTaker timer("generateChunkRaw()");
3852 // Initialize generation
3853 initChunkMake(data, chunkpos);
3858 // Finalize generation
3859 MapChunk *chunk = finishChunkMake(data, changed_blocks);
3862 Return central chunk (which was requested)
3868 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
3869 core::map<v3s16, MapBlock*> &changed_blocks)
3871 dstream<<"generateChunk(): Generating chunk "
3872 <<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
3875 /*for(s16 x=-1; x<=1; x++)
3876 for(s16 y=-1; y<=1; y++)*/
3877 for(s16 x=-0; x<=0; x++)
3878 for(s16 y=-0; y<=0; y++)
3880 v2s16 chunkpos0 = chunkpos1 + v2s16(x,y);
3881 MapChunk *chunk = getChunk(chunkpos0);
3882 // Skip if already generated
3883 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3885 generateChunkRaw(chunkpos0, changed_blocks);
3888 assert(chunkNonVolatile(chunkpos1));
3890 MapChunk *chunk = getChunk(chunkpos1);
3895 ServerMapSector * ServerMap::createSector(v2s16 p2d)
3897 DSTACKF("%s: p2d=(%d,%d)",
3902 Check if it exists already in memory
3904 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3909 Try to load it from disk (with blocks)
3911 if(loadSectorFull(p2d) == true)
3913 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3916 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
3917 throw InvalidPositionException("");
3923 Do not create over-limit
3925 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3926 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3927 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3928 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3929 throw InvalidPositionException("createSector(): pos. over limit");
3932 Generate blank sector
3935 sector = new ServerMapSector(this, p2d);
3937 // Sector position on map in nodes
3938 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3943 m_sectors.insert(p2d, sector);
3949 MapSector * ServerMap::emergeSector(v2s16 p2d,
3950 core::map<v3s16, MapBlock*> &changed_blocks)
3952 DSTACK("%s: p2d=(%d,%d)",
3959 v2s16 chunkpos = sector_to_chunk(p2d);
3960 /*bool chunk_nonvolatile = false;
3961 MapChunk *chunk = getChunk(chunkpos);
3962 if(chunk && chunk->getIsVolatile() == false)
3963 chunk_nonvolatile = true;*/
3964 bool chunk_nonvolatile = chunkNonVolatile(chunkpos);
3967 If chunk is not fully generated, generate chunk
3969 if(chunk_nonvolatile == false)
3971 // Generate chunk and neighbors
3972 generateChunk(chunkpos, changed_blocks);
3976 Return sector if it exists now
3978 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3983 Try to load it from disk
3985 if(loadSectorFull(p2d) == true)
3987 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3990 dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
3991 throw InvalidPositionException("");
3997 generateChunk should have generated the sector
4001 dstream<<"WARNING: ServerMap::emergeSector: Cannot find sector ("
4002 <<p2d.X<<","<<p2d.Y<<" and chunk is already generated. "
4006 dstream<<"WARNING: Creating an empty sector."<<std::endl;
4008 return createSector(p2d);
4013 dstream<<"WARNING: Forcing regeneration of chunk."<<std::endl;
4016 generateChunkRaw(chunkpos, changed_blocks, true);
4019 Return sector if it exists now
4021 sector = getSectorNoGenerateNoEx(p2d);
4025 dstream<<"ERROR: Could not get sector from anywhere."<<std::endl;
4033 //return generateSector();
4038 NOTE: This is not used for main map generation, only for blocks
4039 that are very high or low
4041 MapBlock * ServerMap::generateBlock(
4043 MapBlock *original_dummy,
4044 ServerMapSector *sector,
4045 core::map<v3s16, MapBlock*> &changed_blocks,
4046 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4049 DSTACKF("%s: p=(%d,%d,%d)",
4053 // If chunks are disabled
4054 /*if(m_chunksize == 0)
4056 dstream<<"ServerMap::generateBlock(): Chunks disabled -> "
4057 <<"not generating."<<std::endl;
4061 /*dstream<<"generateBlock(): "
4062 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4065 MapBlock *block = original_dummy;
4067 v2s16 p2d(p.X, p.Z);
4069 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
4072 Do not generate over-limit
4074 if(blockpos_over_limit(p))
4076 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
4077 throw InvalidPositionException("generateBlock(): pos. over limit");
4081 If block doesn't exist, create one.
4082 If it exists, it is a dummy. In that case unDummify() it.
4084 NOTE: This already sets the map as the parent of the block
4088 block = sector->createBlankBlockNoInsert(block_y);
4092 // Remove the block so that nobody can get a half-generated one.
4093 sector->removeBlock(block);
4094 // Allocate the block to contain the generated data
4100 Generate a completely empty block
4102 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4103 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4105 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4109 block->setNode(v3s16(x0,y0,z0), n);
4114 Generate a proper block
4117 u8 water_material = CONTENT_WATERSOURCE;
4119 s32 lowest_ground_y = 32767;
4120 s32 highest_ground_y = -32768;
4122 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4123 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4125 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
4127 //s16 surface_y = 0;
4129 s16 mud_add_amount = get_mud_add_amount(m_seed, p2d_nodes+v2s16(x0,z0));
4131 s16 surface_y = base_rock_level_2d(m_seed, p2d_nodes+v2s16(x0,z0))
4133 // If chunks are disabled
4134 if(m_chunksize == 0)
4135 surface_y = WATER_LEVEL + 1;
4137 if(surface_y < lowest_ground_y)
4138 lowest_ground_y = surface_y;
4139 if(surface_y > highest_ground_y)
4140 highest_ground_y = surface_y;
4142 s32 surface_depth = AVERAGE_MUD_AMOUNT;
4144 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4146 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
4151 NOTE: If there are some man-made structures above the
4152 newly created block, they won't be taken into account.
4154 if(real_y > surface_y)
4155 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
4161 // If node is over heightmap y, it's air or water
4162 if(real_y > surface_y)
4164 // If under water level, it's water
4165 if(real_y < WATER_LEVEL)
4167 n.d = water_material;
4168 n.setLight(LIGHTBANK_DAY,
4169 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
4171 Add to transforming liquid queue (in case it'd
4174 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
4175 m_transforming_liquid.push_back(real_pos);
4181 // Else it's ground or caves (air)
4184 // If it's surface_depth under ground, it's stone
4185 if(real_y <= surface_y - surface_depth)
4187 n.d = CONTENT_STONE;
4191 // It is mud if it is under the first ground
4192 // level or under water
4193 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
4199 n.d = CONTENT_GRASS;
4202 //n.d = CONTENT_MUD;
4204 /*// If under water level, it's mud
4205 if(real_y < WATER_LEVEL)
4207 // Only the topmost node is grass
4208 else if(real_y <= surface_y - 1)
4211 n.d = CONTENT_GRASS;*/
4215 block->setNode(v3s16(x0,y0,z0), n);
4220 Calculate some helper variables
4223 // Completely underground if the highest part of block is under lowest
4225 // This has to be very sure; it's probably one too strict now but
4226 // that's just better.
4227 bool completely_underground =
4228 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
4230 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
4232 bool mostly_underwater_surface = false;
4233 if(highest_ground_y < WATER_LEVEL
4234 && some_part_underground && !completely_underground)
4235 mostly_underwater_surface = true;
4238 Get local attributes
4241 //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
4243 float caves_amount = 0.5;
4248 NOTE: BEWARE: Too big amount of attribute points slows verything
4250 1 interpolation from 5000 points takes 2-3ms.
4252 //TimeTaker timer("generateBlock() local attribute retrieval");
4253 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
4254 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
4255 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
4259 //dstream<<"generateBlock(): Done"<<std::endl;
4265 // Initialize temporary table
4266 const s32 ued = MAP_BLOCKSIZE;
4267 bool underground_emptiness[ued*ued*ued];
4268 for(s32 i=0; i<ued*ued*ued; i++)
4270 underground_emptiness[i] = 0;
4277 Initialize orp and ors. Try to find if some neighboring
4278 MapBlock has a tunnel ended in its side
4282 (float)(myrand()%ued)+0.5,
4283 (float)(myrand()%ued)+0.5,
4284 (float)(myrand()%ued)+0.5
4287 bool found_existing = false;
4293 for(s16 y=0; y<ued; y++)
4294 for(s16 x=0; x<ued; x++)
4296 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4297 if(getNode(ap).d == CONTENT_AIR)
4299 orp = v3f(x+1,y+1,0);
4300 found_existing = true;
4301 goto continue_generating;
4305 catch(InvalidPositionException &e){}
4311 for(s16 y=0; y<ued; y++)
4312 for(s16 x=0; x<ued; x++)
4314 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4315 if(getNode(ap).d == CONTENT_AIR)
4317 orp = v3f(x+1,y+1,ued-1);
4318 found_existing = true;
4319 goto continue_generating;
4323 catch(InvalidPositionException &e){}
4329 for(s16 y=0; y<ued; y++)
4330 for(s16 z=0; z<ued; z++)
4332 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4333 if(getNode(ap).d == CONTENT_AIR)
4335 orp = v3f(0,y+1,z+1);
4336 found_existing = true;
4337 goto continue_generating;
4341 catch(InvalidPositionException &e){}
4347 for(s16 y=0; y<ued; y++)
4348 for(s16 z=0; z<ued; z++)
4350 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4351 if(getNode(ap).d == CONTENT_AIR)
4353 orp = v3f(ued-1,y+1,z+1);
4354 found_existing = true;
4355 goto continue_generating;
4359 catch(InvalidPositionException &e){}
4365 for(s16 x=0; x<ued; x++)
4366 for(s16 z=0; z<ued; z++)
4368 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4369 if(getNode(ap).d == CONTENT_AIR)
4371 orp = v3f(x+1,0,z+1);
4372 found_existing = true;
4373 goto continue_generating;
4377 catch(InvalidPositionException &e){}
4383 for(s16 x=0; x<ued; x++)
4384 for(s16 z=0; z<ued; z++)
4386 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4387 if(getNode(ap).d == CONTENT_AIR)
4389 orp = v3f(x+1,ued-1,z+1);
4390 found_existing = true;
4391 goto continue_generating;
4395 catch(InvalidPositionException &e){}
4397 continue_generating:
4400 Choose whether to actually generate cave
4402 bool do_generate_caves = true;
4403 // Don't generate if no part is underground
4404 if(!some_part_underground)
4406 do_generate_caves = false;
4408 // Don't generate if mostly underwater surface
4409 /*else if(mostly_underwater_surface)
4411 do_generate_caves = false;
4413 // Partly underground = cave
4414 else if(!completely_underground)
4416 do_generate_caves = (rand() % 100 <= (s32)(caves_amount*100));
4418 // Found existing cave underground
4419 else if(found_existing && completely_underground)
4421 do_generate_caves = (rand() % 100 <= (s32)(caves_amount*100));
4423 // Underground and no caves found
4426 do_generate_caves = (rand() % 300 <= (s32)(caves_amount*100));
4429 if(do_generate_caves)
4432 Generate some tunnel starting from orp and ors
4434 for(u16 i=0; i<3; i++)
4437 (float)(myrand()%ued)+0.5,
4438 (float)(myrand()%ued)+0.5,
4439 (float)(myrand()%ued)+0.5
4443 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
4447 for(float f=0; f<1.0; f+=0.04)
4449 v3f fp = orp + vec * f;
4450 v3s16 cp(fp.X, fp.Y, fp.Z);
4452 s16 d1 = d0 + rs - 1;
4453 for(s16 z0=d0; z0<=d1; z0++)
4455 s16 si = rs - abs(z0);
4456 for(s16 x0=-si; x0<=si-1; x0++)
4458 s16 si2 = rs - abs(x0);
4459 for(s16 y0=-si2+1; y0<=si2-1; y0++)
4465 if(isInArea(p, ued) == false)
4467 underground_emptiness[ued*ued*z + ued*y + x] = 1;
4479 // Set to true if has caves.
4480 // Set when some non-air is changed to air when making caves.
4481 bool has_caves = false;
4484 Apply temporary cave data to block
4487 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4488 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4490 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4492 MapNode n = block->getNode(v3s16(x0,y0,z0));
4495 if(underground_emptiness[
4496 ued*ued*(z0*ued/MAP_BLOCKSIZE)
4497 +ued*(y0*ued/MAP_BLOCKSIZE)
4498 +(x0*ued/MAP_BLOCKSIZE)])
4500 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
4509 block->setNode(v3s16(x0,y0,z0), n);
4514 This is used for guessing whether or not the block should
4515 receive sunlight from the top if the block above doesn't exist
4517 block->setIsUnderground(completely_underground);
4520 Force lighting update if some part of block is partly
4521 underground and has caves.
4523 /*if(some_part_underground && !completely_underground && has_caves)
4525 //dstream<<"Half-ground caves"<<std::endl;
4526 lighting_invalidated_blocks[block->getPos()] = block;
4529 // DEBUG: Always update lighting
4530 //lighting_invalidated_blocks[block->getPos()] = block;
4536 if(some_part_underground)
4538 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
4543 for(s16 i=0; i<underground_level/4 + 1; i++)
4545 if(myrand()%50 == 0)
4548 (myrand()%(MAP_BLOCKSIZE-2))+1,
4549 (myrand()%(MAP_BLOCKSIZE-2))+1,
4550 (myrand()%(MAP_BLOCKSIZE-2))+1
4556 for(u16 i=0; i<27; i++)
4558 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4560 block->setNode(cp+g_27dirs[i], n);
4568 u16 coal_amount = 30;
4569 u16 coal_rareness = 60 / coal_amount;
4570 if(coal_rareness == 0)
4572 if(myrand()%coal_rareness == 0)
4574 u16 a = myrand() % 16;
4575 u16 amount = coal_amount * a*a*a / 1000;
4576 for(s16 i=0; i<amount; i++)
4579 (myrand()%(MAP_BLOCKSIZE-2))+1,
4580 (myrand()%(MAP_BLOCKSIZE-2))+1,
4581 (myrand()%(MAP_BLOCKSIZE-2))+1
4585 n.d = CONTENT_STONE;
4586 n.param = MINERAL_COAL;
4588 for(u16 i=0; i<27; i++)
4590 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4592 block->setNode(cp+g_27dirs[i], n);
4600 //TODO: change to iron_amount or whatever
4601 u16 iron_amount = 15;
4602 u16 iron_rareness = 60 / iron_amount;
4603 if(iron_rareness == 0)
4605 if(myrand()%iron_rareness == 0)
4607 u16 a = myrand() % 16;
4608 u16 amount = iron_amount * a*a*a / 1000;
4609 for(s16 i=0; i<amount; i++)
4612 (myrand()%(MAP_BLOCKSIZE-2))+1,
4613 (myrand()%(MAP_BLOCKSIZE-2))+1,
4614 (myrand()%(MAP_BLOCKSIZE-2))+1
4618 n.d = CONTENT_STONE;
4619 n.param = MINERAL_IRON;
4621 for(u16 i=0; i<27; i++)
4623 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4625 block->setNode(cp+g_27dirs[i], n);
4632 Create a few rats in empty blocks underground
4634 if(completely_underground)
4636 //for(u16 i=0; i<2; i++)
4639 (myrand()%(MAP_BLOCKSIZE-2))+1,
4640 (myrand()%(MAP_BLOCKSIZE-2))+1,
4641 (myrand()%(MAP_BLOCKSIZE-2))+1
4644 // Check that the place is empty
4645 //if(!is_ground_content(block->getNode(cp).d))
4648 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp, BS));
4649 block->addObject(obj);
4654 #endif // end of proper block generation
4657 Add block to sector.
4659 sector->insertBlock(block);
4661 // Lighting is invalid after generation.
4662 block->setLightingExpired(true);
4669 <<"lighting_invalidated_blocks.size()"
4673 <<" "<<lighting_invalidated_blocks.size()
4675 <<", "<<completely_underground
4676 <<", "<<some_part_underground
4683 MapBlock * ServerMap::createBlock(v3s16 p)
4685 DSTACKF("%s: p=(%d,%d,%d)",
4686 __FUNCTION_NAME, p.X, p.Y, p.Z);
4689 Do not create over-limit
4691 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4692 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4693 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4694 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4695 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4696 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4697 throw InvalidPositionException("createBlock(): pos. over limit");
4699 v2s16 p2d(p.X, p.Z);
4702 This will create or load a sector if not found in memory.
4703 If block exists on disk, it will be loaded.
4705 NOTE: On old save formats, this will be slow, as it generates
4706 lighting on blocks for them.
4708 ServerMapSector *sector;
4710 sector = (ServerMapSector*)createSector(p2d);
4711 assert(sector->getId() == MAPSECTOR_SERVER);
4713 catch(InvalidPositionException &e)
4715 dstream<<"createBlock: createSector() failed"<<std::endl;
4719 NOTE: This should not be done, or at least the exception
4720 should not be passed on as std::exception, because it
4721 won't be catched at all.
4723 /*catch(std::exception &e)
4725 dstream<<"createBlock: createSector() failed: "
4726 <<e.what()<<std::endl;
4731 Try to get a block from the sector
4734 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4738 block = sector->createBlankBlock(block_y);
4742 MapBlock * ServerMap::emergeBlock(
4744 bool only_from_disk,
4745 core::map<v3s16, MapBlock*> &changed_blocks,
4746 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4749 DSTACKF("%s: p=(%d,%d,%d), only_from_disk=%d",
4751 p.X, p.Y, p.Z, only_from_disk);
4754 Do not generate over-limit
4756 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4757 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4758 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4759 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4760 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4761 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4762 throw InvalidPositionException("emergeBlock(): pos. over limit");
4764 v2s16 p2d(p.X, p.Z);
4767 This will create or load a sector if not found in memory.
4768 If block exists on disk, it will be loaded.
4770 ServerMapSector *sector;
4772 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
4773 assert(sector->getId() == MAPSECTOR_SERVER);
4775 catch(InvalidPositionException &e)
4777 dstream<<"emergeBlock: emergeSector() failed: "
4778 <<e.what()<<std::endl;
4779 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4781 <<"You could try to delete it."<<std::endl;
4784 catch(VersionMismatchException &e)
4786 dstream<<"emergeBlock: emergeSector() failed: "
4787 <<e.what()<<std::endl;
4788 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4790 <<"You could try to delete it."<<std::endl;
4794 NOTE: This should not be done, or at least the exception
4795 should not be passed on as std::exception, because it
4796 won't be catched at all.
4798 /*catch(std::exception &e)
4800 dstream<<"emergeBlock: emergeSector() failed: "
4801 <<e.what()<<std::endl;
4802 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4804 <<"You could try to delete it."<<std::endl;
4809 Try to get a block from the sector
4812 bool does_not_exist = false;
4813 bool lighting_expired = false;
4814 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4818 does_not_exist = true;
4820 else if(block->isDummy() == true)
4822 does_not_exist = true;
4824 else if(block->getLightingExpired())
4826 lighting_expired = true;
4831 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
4836 If block was not found on disk and not going to generate a
4837 new one, make sure there is a dummy block in place.
4839 if(only_from_disk && (does_not_exist || lighting_expired))
4841 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
4845 // Create dummy block
4846 block = new MapBlock(this, p, true);
4848 // Add block to sector
4849 sector->insertBlock(block);
4855 //dstream<<"Not found on disk, generating."<<std::endl;
4857 //TimeTaker("emergeBlock() generate");
4859 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
4862 If the block doesn't exist, generate the block.
4866 block = generateBlock(p, block, sector, changed_blocks,
4867 lighting_invalidated_blocks);
4870 if(lighting_expired)
4872 lighting_invalidated_blocks.insert(p, block);
4876 Initially update sunlight
4880 core::map<v3s16, bool> light_sources;
4881 bool black_air_left = false;
4882 bool bottom_invalid =
4883 block->propagateSunlight(light_sources, true,
4884 &black_air_left, true);
4886 // If sunlight didn't reach everywhere and part of block is
4887 // above ground, lighting has to be properly updated
4888 //if(black_air_left && some_part_underground)
4891 lighting_invalidated_blocks[block->getPos()] = block;
4896 lighting_invalidated_blocks[block->getPos()] = block;
4903 s16 ServerMap::findGroundLevel(v2s16 p2d)
4906 Uh, just do something random...
4908 // Find existing map from top to down
4911 v3s16 p(p2d.X, max, p2d.Y);
4912 for(; p.Y>min; p.Y--)
4914 MapNode n = getNodeNoEx(p);
4915 if(n.d != CONTENT_IGNORE)
4920 // If this node is not air, go to plan b
4921 if(getNodeNoEx(p).d != CONTENT_AIR)
4923 // Search existing walkable and return it
4924 for(; p.Y>min; p.Y--)
4926 MapNode n = getNodeNoEx(p);
4927 if(content_walkable(n.d) && n.d != CONTENT_IGNORE)
4933 Plan B: Get from map generator perlin noise function
4935 // This won't work if proper generation is disabled
4936 if(m_chunksize == 0)
4937 return WATER_LEVEL+2;
4938 double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
4942 void ServerMap::createDirs(std::string path)
4944 if(fs::CreateAllDirs(path) == false)
4946 m_dout<<DTIME<<"ServerMap: Failed to create directory "
4947 <<"\""<<path<<"\""<<std::endl;
4948 throw BaseException("ServerMap failed to create directory");
4952 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
4958 snprintf(cc, 9, "%.4x%.4x",
4959 (unsigned int)pos.X&0xffff,
4960 (unsigned int)pos.Y&0xffff);
4962 return m_savedir + "/sectors/" + cc;
4964 snprintf(cc, 9, "%.3x/%.3x",
4965 (unsigned int)pos.X&0xfff,
4966 (unsigned int)pos.Y&0xfff);
4968 return m_savedir + "/sectors2/" + cc;
4974 v2s16 ServerMap::getSectorPos(std::string dirname)
4978 size_t spos = dirname.rfind('/') + 1;
4979 assert(spos != std::string::npos);
4980 if(dirname.size() - spos == 8)
4983 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
4985 else if(dirname.size() - spos == 3)
4988 r = sscanf(dirname.substr(spos-4).c_str(), "%3x/%3x", &x, &y);
4989 // Sign-extend the 12 bit values up to 16 bits...
4990 if(x&0x800) x|=0xF000;
4991 if(y&0x800) y|=0xF000;
4998 v2s16 pos((s16)x, (s16)y);
5002 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
5004 v2s16 p2d = getSectorPos(sectordir);
5006 if(blockfile.size() != 4){
5007 throw InvalidFilenameException("Invalid block filename");
5010 int r = sscanf(blockfile.c_str(), "%4x", &y);
5012 throw InvalidFilenameException("Invalid block filename");
5013 return v3s16(p2d.X, y, p2d.Y);
5016 void ServerMap::save(bool only_changed)
5018 DSTACK(__FUNCTION_NAME);
5019 if(m_map_saving_enabled == false)
5021 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
5025 if(only_changed == false)
5026 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
5029 if(only_changed == false || m_map_metadata_changed)
5034 // Disable saving chunk metadata if chunks are disabled
5035 if(m_chunksize != 0)
5037 if(only_changed == false || anyChunkModified())
5041 u32 sector_meta_count = 0;
5042 u32 block_count = 0;
5045 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5047 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
5048 for(; i.atEnd() == false; i++)
5050 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
5051 assert(sector->getId() == MAPSECTOR_SERVER);
5053 if(sector->differs_from_disk || only_changed == false)
5055 saveSectorMeta(sector);
5056 sector_meta_count++;
5058 core::list<MapBlock*> blocks;
5059 sector->getBlocks(blocks);
5060 core::list<MapBlock*>::Iterator j;
5061 for(j=blocks.begin(); j!=blocks.end(); j++)
5063 MapBlock *block = *j;
5064 if(block->getChangedFlag() || only_changed == false)
5069 /*dstream<<"ServerMap: Written block ("
5070 <<block->getPos().X<<","
5071 <<block->getPos().Y<<","
5072 <<block->getPos().Z<<")"
5081 Only print if something happened or saved whole map
5083 if(only_changed == false || sector_meta_count != 0
5084 || block_count != 0)
5086 dstream<<DTIME<<"ServerMap: Written: "
5087 <<sector_meta_count<<" sector metadata files, "
5088 <<block_count<<" block files"
5094 // NOTE: Doing this is insane. Deprecated and probably broken.
5095 void ServerMap::loadAll()
5097 DSTACK(__FUNCTION_NAME);
5098 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
5103 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
5105 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
5107 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5110 s32 printed_counter = -100000;
5111 s32 count = list.size();
5113 std::vector<fs::DirListNode>::iterator i;
5114 for(i=list.begin(); i!=list.end(); i++)
5116 if(counter > printed_counter + 10)
5118 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
5119 printed_counter = counter;
5123 MapSector *sector = NULL;
5125 // We want directories
5129 sector = loadSectorMeta(i->name);
5131 catch(InvalidFilenameException &e)
5133 // This catches unknown crap in directory
5136 std::vector<fs::DirListNode> list2 = fs::GetDirListing
5137 (m_savedir+"/sectors/"+i->name);
5138 std::vector<fs::DirListNode>::iterator i2;
5139 for(i2=list2.begin(); i2!=list2.end(); i2++)
5145 loadBlock(i->name, i2->name, sector);
5147 catch(InvalidFilenameException &e)
5149 // This catches unknown crap in directory
5153 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
5158 void ServerMap::saveMasterHeightmap()
5160 DSTACK(__FUNCTION_NAME);
5162 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
5164 createDir(m_savedir);
5166 /*std::string fullpath = m_savedir + "/master_heightmap";
5167 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5168 if(o.good() == false)
5169 throw FileNotGoodException("Cannot open master heightmap");*/
5171 // Format used for writing
5172 //u8 version = SER_FMT_VER_HIGHEST;
5175 void ServerMap::loadMasterHeightmap()
5177 DSTACK(__FUNCTION_NAME);
5179 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
5181 /*std::string fullpath = m_savedir + "/master_heightmap";
5182 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5183 if(is.good() == false)
5184 throw FileNotGoodException("Cannot open master heightmap");*/
5188 void ServerMap::saveMapMeta()
5190 DSTACK(__FUNCTION_NAME);
5192 dstream<<"INFO: ServerMap::saveMapMeta(): "
5193 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
5196 createDirs(m_savedir);
5198 std::string fullpath = m_savedir + "/map_meta.txt";
5199 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
5200 if(os.good() == false)
5202 dstream<<"ERROR: ServerMap::saveMapMeta(): "
5203 <<"could not open"<<fullpath<<std::endl;
5204 throw FileNotGoodException("Cannot open chunk metadata");
5208 params.setU64("seed", m_seed);
5209 params.setS32("chunksize", m_chunksize);
5211 params.writeLines(os);
5213 os<<"[end_of_params]\n";
5215 m_map_metadata_changed = false;
5218 void ServerMap::loadMapMeta()
5220 DSTACK(__FUNCTION_NAME);
5222 dstream<<"INFO: ServerMap::loadMapMeta(): Loading map metadata"
5225 std::string fullpath = m_savedir + "/map_meta.txt";
5226 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5227 if(is.good() == false)
5229 dstream<<"ERROR: ServerMap::loadMapMeta(): "
5230 <<"could not open"<<fullpath<<std::endl;
5231 throw FileNotGoodException("Cannot open map metadata");
5239 throw SerializationError
5240 ("ServerMap::loadMapMeta(): [end_of_params] not found");
5242 std::getline(is, line);
5243 std::string trimmedline = trim(line);
5244 if(trimmedline == "[end_of_params]")
5246 params.parseConfigLine(line);
5249 m_seed = params.getU64("seed");
5250 m_chunksize = params.getS32("chunksize");
5252 dstream<<"INFO: ServerMap::loadMapMeta(): "
5253 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
5257 void ServerMap::saveChunkMeta()
5259 DSTACK(__FUNCTION_NAME);
5261 // This should not be called if chunks are disabled.
5262 assert(m_chunksize != 0);
5264 u32 count = m_chunks.size();
5266 dstream<<"INFO: ServerMap::saveChunkMeta(): Saving metadata of "
5267 <<count<<" chunks"<<std::endl;
5269 createDirs(m_savedir);
5271 std::string fullpath = m_savedir + "/chunk_meta";
5272 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
5273 if(os.good() == false)
5275 dstream<<"ERROR: ServerMap::saveChunkMeta(): "
5276 <<"could not open"<<fullpath<<std::endl;
5277 throw FileNotGoodException("Cannot open chunk metadata");
5283 os.write((char*)&version, 1);
5288 writeU32(buf, count);
5289 os.write((char*)buf, 4);
5291 for(core::map<v2s16, MapChunk*>::Iterator
5292 i = m_chunks.getIterator();
5293 i.atEnd()==false; i++)
5295 v2s16 p = i.getNode()->getKey();
5296 MapChunk *chunk = i.getNode()->getValue();
5299 os.write((char*)buf, 4);
5301 chunk->serialize(os, version);
5304 setChunksNonModified();
5307 void ServerMap::loadChunkMeta()
5309 DSTACK(__FUNCTION_NAME);
5311 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading chunk metadata"
5314 std::string fullpath = m_savedir + "/chunk_meta";
5315 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5316 if(is.good() == false)
5318 dstream<<"ERROR: ServerMap::loadChunkMeta(): "
5319 <<"could not open"<<fullpath<<std::endl;
5320 throw FileNotGoodException("Cannot open chunk metadata");
5326 is.read((char*)&version, 1);
5331 is.read((char*)buf, 4);
5332 u32 count = readU32(buf);
5334 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading metadata of "
5335 <<count<<" chunks"<<std::endl;
5337 for(u32 i=0; i<count; i++)
5340 MapChunk *chunk = new MapChunk();
5342 is.read((char*)buf, 4);
5345 chunk->deSerialize(is, version);
5346 m_chunks.insert(p, chunk);
5350 void ServerMap::saveSectorMeta(ServerMapSector *sector)
5352 DSTACK(__FUNCTION_NAME);
5353 // Format used for writing
5354 u8 version = SER_FMT_VER_HIGHEST;
5356 v2s16 pos = sector->getPos();
5357 std::string dir = getSectorDir(pos);
5360 std::string fullpath = dir + "/meta";
5361 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5362 if(o.good() == false)
5363 throw FileNotGoodException("Cannot open sector metafile");
5365 sector->serialize(o, version);
5367 sector->differs_from_disk = false;
5370 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
5372 DSTACK(__FUNCTION_NAME);
5374 v2s16 p2d = getSectorPos(sectordir);
5376 ServerMapSector *sector = NULL;
5378 std::string fullpath = sectordir + "/meta";
5379 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5380 if(is.good() == false)
5382 // If the directory exists anyway, it probably is in some old
5383 // format. Just go ahead and create the sector.
5384 if(fs::PathExists(sectordir))
5386 dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
5387 <<fullpath<<" doesn't exist but directory does."
5388 <<" Continuing with a sector with no metadata."
5390 sector = new ServerMapSector(this, p2d);
5391 m_sectors.insert(p2d, sector);
5395 throw FileNotGoodException("Cannot open sector metafile");
5400 sector = ServerMapSector::deSerialize
5401 (is, this, p2d, m_sectors);
5403 saveSectorMeta(sector);
5406 sector->differs_from_disk = false;
5411 bool ServerMap::loadSectorFull(v2s16 p2d)
5413 DSTACK(__FUNCTION_NAME);
5415 MapSector *sector = NULL;
5417 // The directory layout we're going to load from.
5418 // 1 - original sectors/xxxxzzzz/
5419 // 2 - new sectors2/xxx/zzz/
5420 // If we load from anything but the latest structure, we will
5421 // immediately save to the new one, and remove the old.
5423 std::string sectordir1 = getSectorDir(p2d, 1);
5424 std::string sectordir;
5425 if(fs::PathExists(sectordir1))
5427 sectordir = sectordir1;
5432 sectordir = getSectorDir(p2d, 2);
5435 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5438 sector = loadSectorMeta(sectordir, loadlayout != 2);
5440 catch(InvalidFilenameException &e)
5444 catch(FileNotGoodException &e)
5448 catch(std::exception &e)
5456 std::vector<fs::DirListNode> list2 = fs::GetDirListing
5458 std::vector<fs::DirListNode>::iterator i2;
5459 for(i2=list2.begin(); i2!=list2.end(); i2++)
5465 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
5467 catch(InvalidFilenameException &e)
5469 // This catches unknown crap in directory
5475 dstream<<"Sector converted to new layout - deleting "<<
5476 sectordir1<<std::endl;
5477 fs::RecursiveDelete(sectordir1);
5484 void ServerMap::saveBlock(MapBlock *block)
5486 DSTACK(__FUNCTION_NAME);
5488 Dummy blocks are not written
5490 if(block->isDummy())
5492 /*v3s16 p = block->getPos();
5493 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
5494 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
5498 // Format used for writing
5499 u8 version = SER_FMT_VER_HIGHEST;
5501 v3s16 p3d = block->getPos();
5502 v2s16 p2d(p3d.X, p3d.Z);
5503 std::string dir = getSectorDir(p2d);
5507 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
5508 std::string fullpath = dir + "/" + cc;
5509 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5510 if(o.good() == false)
5511 throw FileNotGoodException("Cannot open block data");
5514 [0] u8 serialization version
5517 o.write((char*)&version, 1);
5520 block->serialize(o, version);
5522 // Write extra data stored on disk
5523 block->serializeDiskExtra(o, version);
5525 // We just wrote it to the disk so clear modified flag
5526 block->resetChangedFlag();
5529 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
5531 DSTACK(__FUNCTION_NAME);
5533 std::string fullpath = sectordir+"/"+blockfile;
5536 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5537 if(is.good() == false)
5538 throw FileNotGoodException("Cannot open block file");
5540 v3s16 p3d = getBlockPos(sectordir, blockfile);
5541 v2s16 p2d(p3d.X, p3d.Z);
5543 assert(sector->getPos() == p2d);
5545 u8 version = SER_FMT_VER_INVALID;
5546 is.read((char*)&version, 1);
5549 throw SerializationError("ServerMap::loadBlock(): Failed"
5550 " to read MapBlock version");
5552 /*u32 block_size = MapBlock::serializedLength(version);
5553 SharedBuffer<u8> data(block_size);
5554 is.read((char*)*data, block_size);*/
5556 // This will always return a sector because we're the server
5557 //MapSector *sector = emergeSector(p2d);
5559 MapBlock *block = NULL;
5560 bool created_new = false;
5562 block = sector->getBlockNoCreate(p3d.Y);
5564 catch(InvalidPositionException &e)
5566 block = sector->createBlankBlockNoInsert(p3d.Y);
5571 block->deSerialize(is, version);
5573 // Read extra data stored on disk
5574 block->deSerializeDiskExtra(is, version);
5576 // If it's a new block, insert it to the map
5578 sector->insertBlock(block);
5581 Save blocks loaded in old format in new format
5584 if(version < SER_FMT_VER_HIGHEST || save_after_load)
5589 // We just loaded it from the disk, so it's up-to-date.
5590 block->resetChangedFlag();
5593 catch(SerializationError &e)
5595 dstream<<"WARNING: Invalid block data on disk "
5596 "(SerializationError). Ignoring. "
5597 "A new one will be generated."
5600 // TODO: Backup file; name is in fullpath.
5604 void ServerMap::PrintInfo(std::ostream &out)
5615 ClientMap::ClientMap(
5617 MapDrawControl &control,
5618 scene::ISceneNode* parent,
5619 scene::ISceneManager* mgr,
5623 scene::ISceneNode(parent, mgr, id),
5626 m_camera_position(0,0,0),
5627 m_camera_direction(0,0,1)
5629 m_camera_mutex.Init();
5630 assert(m_camera_mutex.IsInitialized());
5632 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
5633 BS*1000000,BS*1000000,BS*1000000);
5636 ClientMap::~ClientMap()
5638 /*JMutexAutoLock lock(mesh_mutex);
5647 MapSector * ClientMap::emergeSector(v2s16 p2d)
5649 DSTACK(__FUNCTION_NAME);
5650 // Check that it doesn't exist already
5652 return getSectorNoGenerate(p2d);
5654 catch(InvalidPositionException &e)
5659 ClientMapSector *sector = new ClientMapSector(this, p2d);
5662 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5663 m_sectors.insert(p2d, sector);
5669 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
5671 DSTACK(__FUNCTION_NAME);
5672 ClientMapSector *sector = NULL;
5674 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5676 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
5680 sector = (ClientMapSector*)n->getValue();
5681 assert(sector->getId() == MAPSECTOR_CLIENT);
5685 sector = new ClientMapSector(this, p2d);
5687 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5688 m_sectors.insert(p2d, sector);
5692 sector->deSerialize(is);
5695 void ClientMap::OnRegisterSceneNode()
5699 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5700 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5703 ISceneNode::OnRegisterSceneNode();
5706 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5708 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5709 DSTACK(__FUNCTION_NAME);
5711 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5714 Get time for measuring timeout.
5716 Measuring time is very useful for long delays when the
5717 machine is swapping a lot.
5719 int time1 = time(0);
5721 //u32 daynight_ratio = m_client->getDayNightRatio();
5723 m_camera_mutex.Lock();
5724 v3f camera_position = m_camera_position;
5725 v3f camera_direction = m_camera_direction;
5726 m_camera_mutex.Unlock();
5729 Get all blocks and draw all visible ones
5732 v3s16 cam_pos_nodes(
5733 camera_position.X / BS,
5734 camera_position.Y / BS,
5735 camera_position.Z / BS);
5737 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5739 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5740 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5742 // Take a fair amount as we will be dropping more out later
5744 p_nodes_min.X / MAP_BLOCKSIZE - 1,
5745 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
5746 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
5748 p_nodes_max.X / MAP_BLOCKSIZE + 1,
5749 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
5750 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
5752 u32 vertex_count = 0;
5754 // For limiting number of mesh updates per frame
5755 u32 mesh_update_count = 0;
5757 u32 blocks_would_have_drawn = 0;
5758 u32 blocks_drawn = 0;
5760 //NOTE: The sectors map should be locked but we're not doing it
5761 // because it'd cause too much delays
5763 int timecheck_counter = 0;
5764 core::map<v2s16, MapSector*>::Iterator si;
5765 si = m_sectors.getIterator();
5766 for(; si.atEnd() == false; si++)
5769 timecheck_counter++;
5770 if(timecheck_counter > 50)
5772 timecheck_counter = 0;
5773 int time2 = time(0);
5774 if(time2 > time1 + 4)
5776 dstream<<"ClientMap::renderMap(): "
5777 "Rendering takes ages, returning."
5784 MapSector *sector = si.getNode()->getValue();
5785 v2s16 sp = sector->getPos();
5787 if(m_control.range_all == false)
5789 if(sp.X < p_blocks_min.X
5790 || sp.X > p_blocks_max.X
5791 || sp.Y < p_blocks_min.Z
5792 || sp.Y > p_blocks_max.Z)
5796 core::list< MapBlock * > sectorblocks;
5797 sector->getBlocks(sectorblocks);
5803 core::list< MapBlock * >::Iterator i;
5804 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5806 MapBlock *block = *i;
5809 Compare block position to camera position, skip
5810 if not seen on display
5813 float range = 100000 * BS;
5814 if(m_control.range_all == false)
5815 range = m_control.wanted_range * BS;
5818 if(isBlockInSight(block->getPos(), camera_position,
5819 camera_direction, range, &d) == false)
5824 // This is ugly (spherical distance limit?)
5825 /*if(m_control.range_all == false &&
5826 d - 0.5*BS*MAP_BLOCKSIZE > range)
5831 Update expired mesh (used for day/night change)
5833 It doesn't work exactly like it should now with the
5834 tasked mesh update but whatever.
5837 bool mesh_expired = false;
5840 JMutexAutoLock lock(block->mesh_mutex);
5842 mesh_expired = block->getMeshExpired();
5844 // Mesh has not been expired and there is no mesh:
5845 // block has no content
5846 if(block->mesh == NULL && mesh_expired == false)
5850 f32 faraway = BS*50;
5851 //f32 faraway = m_control.wanted_range * BS;
5854 This has to be done with the mesh_mutex unlocked
5856 // Pretty random but this should work somewhat nicely
5857 if(mesh_expired && (
5858 (mesh_update_count < 3
5859 && (d < faraway || mesh_update_count < 2)
5862 (m_control.range_all && mesh_update_count < 20)
5865 /*if(mesh_expired && mesh_update_count < 6
5866 && (d < faraway || mesh_update_count < 3))*/
5868 mesh_update_count++;
5870 // Mesh has been expired: generate new mesh
5871 //block->updateMesh(daynight_ratio);
5872 m_client->addUpdateMeshTask(block->getPos());
5874 mesh_expired = false;
5879 Draw the faces of the block
5882 JMutexAutoLock lock(block->mesh_mutex);
5884 scene::SMesh *mesh = block->mesh;
5889 blocks_would_have_drawn++;
5890 if(blocks_drawn >= m_control.wanted_max_blocks
5891 && m_control.range_all == false
5892 && d > m_control.wanted_min_range * BS)
5896 u32 c = mesh->getMeshBufferCount();
5898 for(u32 i=0; i<c; i++)
5900 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
5901 const video::SMaterial& material = buf->getMaterial();
5902 video::IMaterialRenderer* rnd =
5903 driver->getMaterialRenderer(material.MaterialType);
5904 bool transparent = (rnd && rnd->isTransparent());
5905 // Render transparent on transparent pass and likewise.
5906 if(transparent == is_transparent_pass)
5909 This *shouldn't* hurt too much because Irrlicht
5910 doesn't change opengl textures if the old
5911 material is set again.
5913 driver->setMaterial(buf->getMaterial());
5914 driver->drawMeshBuffer(buf);
5915 vertex_count += buf->getVertexCount();
5919 } // foreach sectorblocks
5922 m_control.blocks_drawn = blocks_drawn;
5923 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
5925 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
5926 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
5929 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
5930 core::map<v3s16, MapBlock*> *affected_blocks)
5932 bool changed = false;
5934 Add it to all blocks touching it
5937 v3s16(0,0,0), // this
5938 v3s16(0,0,1), // back
5939 v3s16(0,1,0), // top
5940 v3s16(1,0,0), // right
5941 v3s16(0,0,-1), // front
5942 v3s16(0,-1,0), // bottom
5943 v3s16(-1,0,0), // left
5945 for(u16 i=0; i<7; i++)
5947 v3s16 p2 = p + dirs[i];
5948 // Block position of neighbor (or requested) node
5949 v3s16 blockpos = getNodeBlockPos(p2);
5950 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5951 if(blockref == NULL)
5953 // Relative position of requested node
5954 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5955 if(blockref->setTempMod(relpos, mod))
5960 if(changed && affected_blocks!=NULL)
5962 for(u16 i=0; i<7; i++)
5964 v3s16 p2 = p + dirs[i];
5965 // Block position of neighbor (or requested) node
5966 v3s16 blockpos = getNodeBlockPos(p2);
5967 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5968 if(blockref == NULL)
5970 affected_blocks->insert(blockpos, blockref);
5976 bool ClientMap::clearTempMod(v3s16 p,
5977 core::map<v3s16, MapBlock*> *affected_blocks)
5979 bool changed = false;
5981 v3s16(0,0,0), // this
5982 v3s16(0,0,1), // back
5983 v3s16(0,1,0), // top
5984 v3s16(1,0,0), // right
5985 v3s16(0,0,-1), // front
5986 v3s16(0,-1,0), // bottom
5987 v3s16(-1,0,0), // left
5989 for(u16 i=0; i<7; i++)
5991 v3s16 p2 = p + dirs[i];
5992 // Block position of neighbor (or requested) node
5993 v3s16 blockpos = getNodeBlockPos(p2);
5994 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5995 if(blockref == NULL)
5997 // Relative position of requested node
5998 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5999 if(blockref->clearTempMod(relpos))
6004 if(changed && affected_blocks!=NULL)
6006 for(u16 i=0; i<7; i++)
6008 v3s16 p2 = p + dirs[i];
6009 // Block position of neighbor (or requested) node
6010 v3s16 blockpos = getNodeBlockPos(p2);
6011 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
6012 if(blockref == NULL)
6014 affected_blocks->insert(blockpos, blockref);
6020 void ClientMap::expireMeshes(bool only_daynight_diffed)
6022 TimeTaker timer("expireMeshes()");
6024 core::map<v2s16, MapSector*>::Iterator si;
6025 si = m_sectors.getIterator();
6026 for(; si.atEnd() == false; si++)
6028 MapSector *sector = si.getNode()->getValue();
6030 core::list< MapBlock * > sectorblocks;
6031 sector->getBlocks(sectorblocks);
6033 core::list< MapBlock * >::Iterator i;
6034 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
6036 MapBlock *block = *i;
6038 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
6044 JMutexAutoLock lock(block->mesh_mutex);
6045 if(block->mesh != NULL)
6047 /*block->mesh->drop();
6048 block->mesh = NULL;*/
6049 block->setMeshExpired(true);
6056 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
6058 assert(mapType() == MAPTYPE_CLIENT);
6061 v3s16 p = blockpos + v3s16(0,0,0);
6062 MapBlock *b = getBlockNoCreate(p);
6063 b->updateMesh(daynight_ratio);
6064 //b->setMeshExpired(true);
6066 catch(InvalidPositionException &e){}
6069 v3s16 p = blockpos + v3s16(-1,0,0);
6070 MapBlock *b = getBlockNoCreate(p);
6071 b->updateMesh(daynight_ratio);
6072 //b->setMeshExpired(true);
6074 catch(InvalidPositionException &e){}
6076 v3s16 p = blockpos + v3s16(0,-1,0);
6077 MapBlock *b = getBlockNoCreate(p);
6078 b->updateMesh(daynight_ratio);
6079 //b->setMeshExpired(true);
6081 catch(InvalidPositionException &e){}
6083 v3s16 p = blockpos + v3s16(0,0,-1);
6084 MapBlock *b = getBlockNoCreate(p);
6085 b->updateMesh(daynight_ratio);
6086 //b->setMeshExpired(true);
6088 catch(InvalidPositionException &e){}
6093 Update mesh of block in which the node is, and if the node is at the
6094 leading edge, update the appropriate leading blocks too.
6096 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
6104 v3s16 blockposes[4];
6105 for(u32 i=0; i<4; i++)
6107 v3s16 np = nodepos + dirs[i];
6108 blockposes[i] = getNodeBlockPos(np);
6109 // Don't update mesh of block if it has been done already
6110 bool already_updated = false;
6111 for(u32 j=0; j<i; j++)
6113 if(blockposes[j] == blockposes[i])
6115 already_updated = true;
6122 MapBlock *b = getBlockNoCreate(blockposes[i]);
6123 b->updateMesh(daynight_ratio);
6128 void ClientMap::PrintInfo(std::ostream &out)
6139 MapVoxelManipulator::MapVoxelManipulator(Map *map)
6144 MapVoxelManipulator::~MapVoxelManipulator()
6146 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
6150 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
6152 TimeTaker timer1("emerge", &emerge_time);
6154 // Units of these are MapBlocks
6155 v3s16 p_min = getNodeBlockPos(a.MinEdge);
6156 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
6158 VoxelArea block_area_nodes
6159 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6161 addArea(block_area_nodes);
6163 for(s32 z=p_min.Z; z<=p_max.Z; z++)
6164 for(s32 y=p_min.Y; y<=p_max.Y; y++)
6165 for(s32 x=p_min.X; x<=p_max.X; x++)
6168 core::map<v3s16, bool>::Node *n;
6169 n = m_loaded_blocks.find(p);
6173 bool block_data_inexistent = false;
6176 TimeTaker timer1("emerge load", &emerge_load_time);
6178 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
6179 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
6182 dstream<<std::endl;*/
6184 MapBlock *block = m_map->getBlockNoCreate(p);
6185 if(block->isDummy())
6186 block_data_inexistent = true;
6188 block->copyTo(*this);
6190 catch(InvalidPositionException &e)
6192 block_data_inexistent = true;
6195 if(block_data_inexistent)
6197 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6198 // Fill with VOXELFLAG_INEXISTENT
6199 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
6200 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
6202 s32 i = m_area.index(a.MinEdge.X,y,z);
6203 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
6207 m_loaded_blocks.insert(p, !block_data_inexistent);
6210 //dstream<<"emerge done"<<std::endl;
6214 SUGG: Add an option to only update eg. water and air nodes.
6215 This will make it interfere less with important stuff if
6218 void MapVoxelManipulator::blitBack
6219 (core::map<v3s16, MapBlock*> & modified_blocks)
6221 if(m_area.getExtent() == v3s16(0,0,0))
6224 //TimeTaker timer1("blitBack");
6226 /*dstream<<"blitBack(): m_loaded_blocks.size()="
6227 <<m_loaded_blocks.size()<<std::endl;*/
6230 Initialize block cache
6232 v3s16 blockpos_last;
6233 MapBlock *block = NULL;
6234 bool block_checked_in_modified = false;
6236 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
6237 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
6238 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
6242 u8 f = m_flags[m_area.index(p)];
6243 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
6246 MapNode &n = m_data[m_area.index(p)];
6248 v3s16 blockpos = getNodeBlockPos(p);
6253 if(block == NULL || blockpos != blockpos_last){
6254 block = m_map->getBlockNoCreate(blockpos);
6255 blockpos_last = blockpos;
6256 block_checked_in_modified = false;
6259 // Calculate relative position in block
6260 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
6262 // Don't continue if nothing has changed here
6263 if(block->getNode(relpos) == n)
6266 //m_map->setNode(m_area.MinEdge + p, n);
6267 block->setNode(relpos, n);
6270 Make sure block is in modified_blocks
6272 if(block_checked_in_modified == false)
6274 modified_blocks[blockpos] = block;
6275 block_checked_in_modified = true;
6278 catch(InvalidPositionException &e)
6284 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
6285 MapVoxelManipulator(map),
6286 m_create_area(false)
6290 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
6294 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
6296 // Just create the area so that it can be pointed to
6297 VoxelManipulator::emerge(a, caller_id);
6300 void ManualMapVoxelManipulator::initialEmerge(
6301 v3s16 blockpos_min, v3s16 blockpos_max)
6303 TimeTaker timer1("initialEmerge", &emerge_time);
6305 // Units of these are MapBlocks
6306 v3s16 p_min = blockpos_min;
6307 v3s16 p_max = blockpos_max;
6309 VoxelArea block_area_nodes
6310 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6312 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
6315 dstream<<"initialEmerge: area: ";
6316 block_area_nodes.print(dstream);
6317 dstream<<" ("<<size_MB<<"MB)";
6321 addArea(block_area_nodes);
6323 for(s32 z=p_min.Z; z<=p_max.Z; z++)
6324 for(s32 y=p_min.Y; y<=p_max.Y; y++)
6325 for(s32 x=p_min.X; x<=p_max.X; x++)
6328 core::map<v3s16, bool>::Node *n;
6329 n = m_loaded_blocks.find(p);
6333 bool block_data_inexistent = false;
6336 TimeTaker timer1("emerge load", &emerge_load_time);
6338 MapBlock *block = m_map->getBlockNoCreate(p);
6339 if(block->isDummy())
6340 block_data_inexistent = true;
6342 block->copyTo(*this);
6344 catch(InvalidPositionException &e)
6346 block_data_inexistent = true;
6349 if(block_data_inexistent)
6352 Mark area inexistent
6354 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6355 // Fill with VOXELFLAG_INEXISTENT
6356 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
6357 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
6359 s32 i = m_area.index(a.MinEdge.X,y,z);
6360 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
6364 m_loaded_blocks.insert(p, !block_data_inexistent);
6368 void ManualMapVoxelManipulator::blitBackAll(
6369 core::map<v3s16, MapBlock*> * modified_blocks)
6371 if(m_area.getExtent() == v3s16(0,0,0))
6375 Copy data of all blocks
6377 for(core::map<v3s16, bool>::Iterator
6378 i = m_loaded_blocks.getIterator();
6379 i.atEnd() == false; i++)
6381 bool existed = i.getNode()->getValue();
6382 if(existed == false)
6384 v3s16 p = i.getNode()->getKey();
6385 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
6388 dstream<<"WARNING: "<<__FUNCTION_NAME
6389 <<": got NULL block "
6390 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
6395 block->copyFrom(*this);
6398 modified_blocks->insert(p, block);