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_cactus(VoxelManipulator &vmanip, v3s16 p0)
2050 MapNode cactusnode(CONTENT_CACTUS);
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)] = cactusnode;
2063 Noise functions. Make sure seed is mangled differently in each one.
2066 // Amount of trees per area in nodes
2067 double tree_amount_2d(u64 seed, v2s16 p)
2069 double noise = noise2d_perlin(
2070 0.5+(float)p.X/250, 0.5+(float)p.Y/250,
2072 double zeroval = -0.3;
2076 return 0.04 * (noise-zeroval) / (1.0-zeroval);
2079 #define AVERAGE_MUD_AMOUNT 4
2081 double base_rock_level_2d(u64 seed, v2s16 p)
2083 // The base ground level
2084 double base = (double)WATER_LEVEL - (double)AVERAGE_MUD_AMOUNT
2085 + 20. * noise2d_perlin(
2086 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2087 (seed>>32)+654879876, 6, 0.6);
2089 /*// A bit hillier one
2090 double base2 = WATER_LEVEL - 4.0 + 40. * noise2d_perlin(
2091 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2092 (seed>>27)+90340, 6, 0.69);
2096 // Higher ground level
2097 double higher = (double)WATER_LEVEL + 25. + 35. * noise2d_perlin(
2098 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2099 seed+85039, 5, 0.69);
2100 //higher = 30; // For debugging
2102 // Limit higher to at least base
2106 // Steepness factor of cliffs
2107 double b = 1.0 + 1.0 * noise2d_perlin(
2108 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2110 b = rangelim(b, 0.0, 1000.0);
2113 b = rangelim(b, 3.0, 1000.0);
2114 //dstream<<"b="<<b<<std::endl;
2117 // Offset to more low
2118 double a_off = -0.2;
2119 // High/low selector
2120 /*double a = 0.5 + b * (a_off + noise2d_perlin(
2121 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2122 seed-359, 6, 0.7));*/
2123 double a = (double)0.5 + b * (a_off + noise2d_perlin(
2124 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2125 seed-359, 5, 0.60));
2127 a = rangelim(a, 0.0, 1.0);
2129 //dstream<<"a="<<a<<std::endl;
2131 double h = base*(1.0-a) + higher*a;
2138 double get_mud_add_amount(u64 seed, v2s16 p)
2140 return ((float)AVERAGE_MUD_AMOUNT + 3.0 * noise2d_perlin(
2141 0.5+(float)p.X/200, 0.5+(float)p.Y/200,
2142 seed+91013, 3, 0.55));
2146 Adds random objects to block, depending on the content of the block
2148 void addRandomObjects(MapBlock *block)
2150 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2151 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2153 bool last_node_walkable = false;
2154 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2157 MapNode n = block->getNodeNoEx(p);
2158 if(n.d == CONTENT_IGNORE)
2160 if(content_features(n.d).liquid_type != LIQUID_NONE)
2162 if(content_features(n.d).walkable)
2164 last_node_walkable = true;
2167 if(last_node_walkable)
2169 // If block contains light information
2170 if(content_features(n.d).param_type == CPT_LIGHT)
2172 if(n.getLight(LIGHTBANK_DAY) <= 3)
2174 if(myrand() % 300 == 0)
2176 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
2178 ServerActiveObject *obj = new RatSAO(NULL, 0, pos_f);
2179 std::string data = obj->getStaticData();
2180 StaticObject s_obj(obj->getType(),
2181 obj->getBasePosition(), data);
2183 block->m_static_objects.insert(0, s_obj);
2184 block->m_static_objects.insert(0, s_obj);
2185 block->m_static_objects.insert(0, s_obj);
2186 block->m_static_objects.insert(0, s_obj);
2187 block->m_static_objects.insert(0, s_obj);
2188 block->m_static_objects.insert(0, s_obj);
2191 if(myrand() % 300 == 0)
2193 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
2195 ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f);
2196 std::string data = obj->getStaticData();
2197 StaticObject s_obj(obj->getType(),
2198 obj->getBasePosition(), data);
2200 block->m_static_objects.insert(0, s_obj);
2206 last_node_walkable = false;
2209 block->setChangedFlag();
2212 #define VMANIP_FLAG_DUNGEON VOXELFLAG_CHECKED1
2215 This is the main map generation method
2218 void makeChunk(ChunkMakeData *data)
2223 s16 y_nodes_min = data->y_blocks_min * MAP_BLOCKSIZE;
2224 s16 y_nodes_max = data->y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
2225 s16 h_blocks = data->y_blocks_max - data->y_blocks_min + 1;
2226 u32 relative_volume = (u32)data->sectorpos_base_size*MAP_BLOCKSIZE
2227 *(u32)data->sectorpos_base_size*MAP_BLOCKSIZE
2228 *(u32)h_blocks*MAP_BLOCKSIZE;
2229 v3s16 bigarea_blocks_min(
2230 data->sectorpos_bigbase.X,
2232 data->sectorpos_bigbase.Y
2234 v3s16 bigarea_blocks_max(
2235 data->sectorpos_bigbase.X + data->sectorpos_bigbase_size - 1,
2237 data->sectorpos_bigbase.Y + data->sectorpos_bigbase_size - 1
2239 s16 lighting_min_d = 0-data->max_spread_amount;
2240 s16 lighting_max_d = data->sectorpos_base_size*MAP_BLOCKSIZE
2241 + data->max_spread_amount-1;
2244 data->vmanip.clearFlag(0xff);
2246 TimeTaker timer_generate("makeChunk() generate");
2248 // Maximum height of the stone surface and obstacles.
2249 // This is used to disable cave generation from going too high.
2250 s16 stone_surface_max_y = 0;
2253 Generate general ground level to full area
2257 TimeTaker timer1("Generating ground level");
2260 NoiseBuffer noisebuf1;
2261 //NoiseBuffer noisebuf2;
2264 data->sectorpos_bigbase.X*MAP_BLOCKSIZE,
2266 data->sectorpos_bigbase.Y*MAP_BLOCKSIZE
2268 v3f maxpos_f = minpos_f + v3f(
2269 data->sectorpos_bigbase_size*MAP_BLOCKSIZE,
2270 y_nodes_max-y_nodes_min,
2271 data->sectorpos_bigbase_size*MAP_BLOCKSIZE
2273 v3f samplelength_f = v3f(4.0, 4.0, 4.0);
2275 TimeTaker timer("noisebuf.create");
2277 noisebuf1.create(data->seed+25104, 6, 0.60, 200.0,
2278 minpos_f.X, minpos_f.Y, minpos_f.Z,
2279 maxpos_f.X, maxpos_f.Y, maxpos_f.Z,
2280 samplelength_f.X, samplelength_f.Y, samplelength_f.Z);
2281 /*noisebuf1.create(data->seed+25104, 3, 0.60, 25.0,
2282 minpos_f.X, minpos_f.Y, minpos_f.Z,
2283 maxpos_f.X, maxpos_f.Y, maxpos_f.Z,
2284 samplelength_f.X, samplelength_f.Y, samplelength_f.Z);
2285 noisebuf2.create(data->seed+25105, 4, 0.50, 200.0,
2286 minpos_f.X, minpos_f.Y, minpos_f.Z,
2287 maxpos_f.X, maxpos_f.Y, maxpos_f.Z,
2288 samplelength_f.X, samplelength_f.Y, samplelength_f.Z);*/
2291 for(s16 x=0; x<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2292 for(s16 z=0; z<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2295 v2s16 p2d = data->sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2297 // Ground height at this point
2298 float surface_y_f = 0.0;
2300 // Use perlin noise for ground height
2301 surface_y_f = base_rock_level_2d(data->seed, p2d);
2302 //surface_y_f = base_rock_level_2d(data->seed, p2d);
2304 // Convert to integer
2305 s16 surface_y = (s16)surface_y_f;
2308 if(surface_y > stone_surface_max_y)
2309 stone_surface_max_y = surface_y;
2312 Fill ground with stone
2315 // Use fast index incrementing
2316 v3s16 em = data->vmanip.m_area.getExtent();
2317 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2318 for(s16 y=y_nodes_min; y<=y_nodes_max; y++)
2320 // Skip if already generated.
2321 // This is done here because there might be a cave at
2322 // any point in ground, which could look like it
2323 // wasn't generated.
2324 if(data->vmanip.m_data[i].d != CONTENT_AIR)
2327 /*s16 noiseval = 50.0 * noise3d_perlin(
2328 0.5+(float)p2d.X/100.0,
2330 0.5+(float)p2d.Y/100.0,
2331 data->seed+123, 5, 0.5);*/
2332 double noiseval = 64.0 * noisebuf1.get(p2d.X, y, p2d.Y);
2333 /*double noiseval = 30.0 * noisebuf1.get(p2d.X, y, p2d.Y);
2334 noiseval *= MYMAX(0, -0.2 + noisebuf2.get(p2d.X, y, p2d.Y));*/
2336 //if(y < surface_y + noiseval)
2339 data->vmanip.m_data[i].d = CONTENT_STONE;
2341 data->vmanip.m_area.add_y(em, i, 1);
2348 for(s16 x=0; x<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2349 for(s16 z=0; z<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2352 v2s16 p2d = data->sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2355 Skip of already generated
2358 v3s16 p(p2d.X, y_nodes_min, p2d.Y);
2359 if(data->vmanip.m_data[data->vmanip.m_area.index(p)].d != CONTENT_AIR)
2363 // Ground height at this point
2364 float surface_y_f = 0.0;
2366 // Use perlin noise for ground height
2367 surface_y_f = base_rock_level_2d(data->seed, p2d);
2369 /*// Experimental stuff
2371 float a = highlands_level_2d(data->seed, p2d);
2376 // Convert to integer
2377 s16 surface_y = (s16)surface_y_f;
2380 if(surface_y > stone_surface_max_y)
2381 stone_surface_max_y = surface_y;
2384 Fill ground with stone
2387 // Use fast index incrementing
2388 v3s16 em = data->vmanip.m_area.getExtent();
2389 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2390 for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++)
2392 // Skip if already generated.
2393 // This is done here because there might be a cave at
2394 // any point in ground, which could look like it
2395 // wasn't generated.
2396 if(data->vmanip.m_data[i].d != CONTENT_AIR)
2399 data->vmanip.m_data[i].d = CONTENT_STONE;
2401 data->vmanip.m_area.add_y(em, i, 1);
2410 Randomize some parameters
2413 //s32 stone_obstacle_count = 0;
2414 /*s32 stone_obstacle_count =
2415 rangelim((1.0+noise2d(data->seed+897,
2416 data->sectorpos_base.X, data->sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2418 //s16 stone_obstacle_max_height = 0;
2419 /*s16 stone_obstacle_max_height =
2420 rangelim((1.0+noise2d(data->seed+5902,
2421 data->sectorpos_base.X, data->sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2424 Loop this part, it will make stuff look older and newer nicely
2426 const u32 age_loops = 2;
2427 for(u32 i_age=0; i_age<age_loops; i_age++)
2429 /******************************
2430 BEGINNING OF AGING LOOP
2431 ******************************/
2436 //TimeTaker timer1("caves");
2441 u32 caves_count = relative_volume / 400000;
2442 u32 bruises_count = relative_volume * stone_surface_max_y / 40000000;
2443 if(stone_surface_max_y < WATER_LEVEL)
2445 /*u32 caves_count = 0;
2446 u32 bruises_count = 0;*/
2447 for(u32 jj=0; jj<caves_count+bruises_count; jj++)
2449 s16 min_tunnel_diameter = 3;
2450 s16 max_tunnel_diameter = 5;
2451 u16 tunnel_routepoints = 20;
2453 v3f main_direction(0,0,0);
2455 bool bruise_surface = (jj > caves_count);
2459 min_tunnel_diameter = 5;
2460 max_tunnel_diameter = myrand_range(10, 20);
2461 /*min_tunnel_diameter = MYMAX(0, stone_surface_max_y/6);
2462 max_tunnel_diameter = myrand_range(MYMAX(0, stone_surface_max_y/6), MYMAX(0, stone_surface_max_y/2));*/
2464 /*s16 tunnel_rou = rangelim(25*(0.5+1.0*noise2d(data->seed+42,
2465 data->sectorpos_base.X, data->sectorpos_base.Y)), 0, 15);*/
2467 tunnel_routepoints = 5;
2473 // Allowed route area size in nodes
2475 data->sectorpos_base_size*MAP_BLOCKSIZE,
2476 h_blocks*MAP_BLOCKSIZE,
2477 data->sectorpos_base_size*MAP_BLOCKSIZE
2480 // Area starting point in nodes
2482 data->sectorpos_base.X*MAP_BLOCKSIZE,
2483 data->y_blocks_min*MAP_BLOCKSIZE,
2484 data->sectorpos_base.Y*MAP_BLOCKSIZE
2488 //(this should be more than the maximum radius of the tunnel)
2489 //s16 insure = 5; // Didn't work with max_d = 20
2491 s16 more = data->max_spread_amount - max_tunnel_diameter/2 - insure;
2492 ar += v3s16(1,0,1) * more * 2;
2493 of -= v3s16(1,0,1) * more;
2495 s16 route_y_min = 0;
2496 // Allow half a diameter + 7 over stone surface
2497 s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2 + 7;
2499 /*// If caves, don't go through surface too often
2500 if(bruise_surface == false)
2501 route_y_max -= myrand_range(0, max_tunnel_diameter*2);*/
2503 // Limit maximum to area
2504 route_y_max = rangelim(route_y_max, 0, ar.Y-1);
2508 /*// Minimum is at y=0
2509 route_y_min = -of.Y - 0;*/
2510 // Minimum is at y=max_tunnel_diameter/4
2511 //route_y_min = -of.Y + max_tunnel_diameter/4;
2512 //s16 min = -of.Y + max_tunnel_diameter/4;
2513 s16 min = -of.Y + 0;
2514 route_y_min = myrand_range(min, min + max_tunnel_diameter);
2515 route_y_min = rangelim(route_y_min, 0, route_y_max);
2518 /*dstream<<"route_y_min = "<<route_y_min
2519 <<", route_y_max = "<<route_y_max<<std::endl;*/
2521 s16 route_start_y_min = route_y_min;
2522 s16 route_start_y_max = route_y_max;
2524 // Start every 2nd cave from surface
2525 bool coming_from_surface = (jj % 2 == 0 && bruise_surface == false);
2527 if(coming_from_surface)
2529 route_start_y_min = -of.Y + stone_surface_max_y + 10;
2532 route_start_y_min = rangelim(route_start_y_min, 0, ar.Y-1);
2533 route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y-1);
2535 // Randomize starting position
2537 (float)(myrand()%ar.X)+0.5,
2538 (float)(myrand_range(route_start_y_min, route_start_y_max))+0.5,
2539 (float)(myrand()%ar.Z)+0.5
2542 MapNode airnode(CONTENT_AIR);
2545 Generate some tunnel starting from orp
2548 for(u16 j=0; j<tunnel_routepoints; j++)
2550 if(j%7==0 && bruise_surface == false)
2552 main_direction = v3f(
2553 ((float)(myrand()%20)-(float)10)/10,
2554 ((float)(myrand()%20)-(float)10)/30,
2555 ((float)(myrand()%20)-(float)10)/10
2557 main_direction *= (float)myrand_range(1, 3);
2561 s16 min_d = min_tunnel_diameter;
2562 s16 max_d = max_tunnel_diameter;
2563 s16 rs = myrand_range(min_d, max_d);
2568 maxlen = v3s16(rs*7,rs*7,rs*7);
2572 maxlen = v3s16(rs*4, myrand_range(1, rs*3), rs*4);
2577 if(coming_from_surface && j < 3)
2580 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2581 (float)(myrand()%(maxlen.Y*1))-(float)maxlen.Y,
2582 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2588 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2589 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2590 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2594 vec += main_direction;
2599 else if(rp.X >= ar.X)
2601 if(rp.Y < route_y_min)
2603 else if(rp.Y >= route_y_max)
2604 rp.Y = route_y_max-1;
2607 else if(rp.Z >= ar.Z)
2611 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2613 v3f fp = orp + vec * f;
2614 v3s16 cp(fp.X, fp.Y, fp.Z);
2617 s16 d1 = d0 + rs - 1;
2618 for(s16 z0=d0; z0<=d1; z0++)
2620 //s16 si = rs - MYMAX(0, abs(z0)-rs/4);
2621 s16 si = rs - MYMAX(0, abs(z0)-rs/7);
2622 for(s16 x0=-si; x0<=si-1; x0++)
2624 s16 maxabsxz = MYMAX(abs(x0), abs(z0));
2625 //s16 si2 = rs - MYMAX(0, maxabsxz-rs/4);
2626 s16 si2 = rs - MYMAX(0, maxabsxz-rs/7);
2627 //s16 si2 = rs - abs(x0);
2628 for(s16 y0=-si2+1+2; y0<=si2-1; y0++)
2634 /*if(isInArea(p, ar) == false)
2636 // Check only height
2637 if(y < 0 || y >= ar.Y)
2641 //assert(data->vmanip.m_area.contains(p));
2642 if(data->vmanip.m_area.contains(p) == false)
2644 dstream<<"WARNING: "<<__FUNCTION_NAME
2645 <<":"<<__LINE__<<": "
2646 <<"point not in area"
2651 // Just set it to air, it will be changed to
2653 u32 i = data->vmanip.m_area.index(p);
2654 data->vmanip.m_data[i] = airnode;
2656 if(bruise_surface == false)
2659 data->vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON;
2677 //TimeTaker timer1("ore veins");
2682 for(u32 jj=0; jj<relative_volume/1000; jj++)
2684 s16 max_vein_diameter = 3;
2686 // Allowed route area size in nodes
2688 data->sectorpos_base_size*MAP_BLOCKSIZE,
2689 h_blocks*MAP_BLOCKSIZE,
2690 data->sectorpos_base_size*MAP_BLOCKSIZE
2693 // Area starting point in nodes
2695 data->sectorpos_base.X*MAP_BLOCKSIZE,
2696 data->y_blocks_min*MAP_BLOCKSIZE,
2697 data->sectorpos_base.Y*MAP_BLOCKSIZE
2701 //(this should be more than the maximum radius of the tunnel)
2703 s16 more = data->max_spread_amount - max_vein_diameter/2 - insure;
2704 ar += v3s16(1,0,1) * more * 2;
2705 of -= v3s16(1,0,1) * more;
2707 // Randomize starting position
2709 (float)(myrand()%ar.X)+0.5,
2710 (float)(myrand()%ar.Y)+0.5,
2711 (float)(myrand()%ar.Z)+0.5
2714 // Randomize mineral
2717 mineral = MINERAL_COAL;
2719 mineral = MINERAL_IRON;
2722 Generate some vein starting from orp
2725 for(u16 j=0; j<2; j++)
2728 (float)(myrand()%ar.X)+0.5,
2729 (float)(myrand()%ar.Y)+0.5,
2730 (float)(myrand()%ar.Z)+0.5
2732 v3f vec = rp - orp;*/
2734 v3s16 maxlen(5, 5, 5);
2736 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2737 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2738 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2743 else if(rp.X >= ar.X)
2747 else if(rp.Y >= ar.Y)
2751 else if(rp.Z >= ar.Z)
2757 s16 max_d = max_vein_diameter;
2758 s16 rs = myrand_range(min_d, max_d);
2760 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2762 v3f fp = orp + vec * f;
2763 v3s16 cp(fp.X, fp.Y, fp.Z);
2765 s16 d1 = d0 + rs - 1;
2766 for(s16 z0=d0; z0<=d1; z0++)
2768 s16 si = rs - abs(z0);
2769 for(s16 x0=-si; x0<=si-1; x0++)
2771 s16 si2 = rs - abs(x0);
2772 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2774 // Don't put mineral to every place
2782 /*if(isInArea(p, ar) == false)
2784 // Check only height
2785 if(y < 0 || y >= ar.Y)
2789 assert(data->vmanip.m_area.contains(p));
2791 // Just set it to air, it will be changed to
2793 u32 i = data->vmanip.m_area.index(p);
2794 MapNode *n = &data->vmanip.m_data[i];
2795 if(n->d == CONTENT_STONE)
2813 TimeTaker timer1("add mud");
2816 Add mud to the central chunk
2819 for(s16 x=0; x<data->sectorpos_base_size*MAP_BLOCKSIZE; x++)
2820 for(s16 z=0; z<data->sectorpos_base_size*MAP_BLOCKSIZE; z++)
2822 // Node position in 2d
2823 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2825 // Randomize mud amount
2826 s16 mud_add_amount = get_mud_add_amount(data->seed, p2d) / 2.0;
2828 // Find ground level
2829 s16 surface_y = find_ground_level_clever(data->vmanip, p2d);
2832 If topmost node is grass, change it to mud.
2833 It might be if it was flown to there from a neighboring
2834 chunk and then converted.
2837 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y));
2838 MapNode *n = &data->vmanip.m_data[i];
2839 if(n->d == CONTENT_GRASS)
2840 *n = MapNode(CONTENT_MUD);
2841 //n->d = CONTENT_MUD;
2849 v3s16 em = data->vmanip.m_area.getExtent();
2850 s16 y_start = surface_y+1;
2851 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2852 for(s16 y=y_start; y<=y_nodes_max; y++)
2854 if(mudcount >= mud_add_amount)
2857 MapNode &n = data->vmanip.m_data[i];
2858 n = MapNode(CONTENT_MUD);
2859 //n.d = CONTENT_MUD;
2862 data->vmanip.m_area.add_y(em, i, 1);
2874 TimeTaker timer1("flow mud");
2877 Flow mud away from steep edges
2880 // Limit area by 1 because mud is flown into neighbors.
2881 s16 mudflow_minpos = 0-data->max_spread_amount+1;
2882 s16 mudflow_maxpos = data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-2;
2884 // Iterate a few times
2885 for(s16 k=0; k<3; k++)
2888 for(s16 x=mudflow_minpos;
2891 for(s16 z=mudflow_minpos;
2895 // Invert coordinates every 2nd iteration
2898 x = mudflow_maxpos - (x-mudflow_minpos);
2899 z = mudflow_maxpos - (z-mudflow_minpos);
2902 // Node position in 2d
2903 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2905 v3s16 em = data->vmanip.m_area.getExtent();
2906 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2913 for(; y>=y_nodes_min; y--)
2915 n = &data->vmanip.m_data[i];
2916 //if(content_walkable(n->d))
2918 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2921 data->vmanip.m_area.add_y(em, i, -1);
2924 // Stop if out of area
2925 //if(data->vmanip.m_area.contains(i) == false)
2929 /*// If not mud, do nothing to it
2930 MapNode *n = &data->vmanip.m_data[i];
2931 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
2935 Don't flow it if the stuff under it is not mud
2939 data->vmanip.m_area.add_y(em, i2, -1);
2940 // Cancel if out of area
2941 if(data->vmanip.m_area.contains(i2) == false)
2943 MapNode *n2 = &data->vmanip.m_data[i2];
2944 if(n2->d != CONTENT_MUD && n2->d != CONTENT_GRASS)
2948 // Make it exactly mud
2951 /*s16 recurse_count = 0;
2955 v3s16(0,0,1), // back
2956 v3s16(1,0,0), // right
2957 v3s16(0,0,-1), // front
2958 v3s16(-1,0,0), // left
2961 // Theck that upper is air or doesn't exist.
2962 // Cancel dropping if upper keeps it in place
2964 data->vmanip.m_area.add_y(em, i3, 1);
2965 if(data->vmanip.m_area.contains(i3) == true
2966 && content_walkable(data->vmanip.m_data[i3].d) == true)
2973 for(u32 di=0; di<4; di++)
2975 v3s16 dirp = dirs4[di];
2978 data->vmanip.m_area.add_p(em, i2, dirp);
2979 // Fail if out of area
2980 if(data->vmanip.m_area.contains(i2) == false)
2982 // Check that side is air
2983 MapNode *n2 = &data->vmanip.m_data[i2];
2984 if(content_walkable(n2->d))
2986 // Check that under side is air
2987 data->vmanip.m_area.add_y(em, i2, -1);
2988 if(data->vmanip.m_area.contains(i2) == false)
2990 n2 = &data->vmanip.m_data[i2];
2991 if(content_walkable(n2->d))
2993 /*// Check that under that is air (need a drop of 2)
2994 data->vmanip.m_area.add_y(em, i2, -1);
2995 if(data->vmanip.m_area.contains(i2) == false)
2997 n2 = &data->vmanip.m_data[i2];
2998 if(content_walkable(n2->d))
3000 // Loop further down until not air
3002 data->vmanip.m_area.add_y(em, i2, -1);
3003 // Fail if out of area
3004 if(data->vmanip.m_area.contains(i2) == false)
3006 n2 = &data->vmanip.m_data[i2];
3007 }while(content_walkable(n2->d) == false);
3008 // Loop one up so that we're in air
3009 data->vmanip.m_area.add_y(em, i2, 1);
3010 n2 = &data->vmanip.m_data[i2];
3012 // Move mud to new place
3014 // Set old place to be air
3015 *n = MapNode(CONTENT_AIR);
3031 TimeTaker timer1("add water");
3034 Add water to the central chunk (and a bit more)
3037 for(s16 x=0-data->max_spread_amount;
3038 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3040 for(s16 z=0-data->max_spread_amount;
3041 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3044 // Node position in 2d
3045 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3047 // Find ground level
3048 //s16 surface_y = find_ground_level(data->vmanip, p2d);
3051 If ground level is over water level, skip.
3052 NOTE: This leaves caves near water without water,
3053 which looks especially crappy when the nearby water
3054 won't start flowing either for some reason
3056 /*if(surface_y > WATER_LEVEL)
3063 v3s16 em = data->vmanip.m_area.getExtent();
3064 u8 light = LIGHT_MAX;
3065 // Start at global water surface level
3066 s16 y_start = WATER_LEVEL;
3067 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3068 MapNode *n = &data->vmanip.m_data[i];
3070 for(s16 y=y_start; y>=y_nodes_min; y--)
3072 n = &data->vmanip.m_data[i];
3074 // Stop when there is no water and no air
3075 if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
3076 && n->d != CONTENT_WATER)
3082 // Make water only not in caves
3083 if(!(data->vmanip.m_flags[i]&VMANIP_FLAG_DUNGEON))
3085 n->d = CONTENT_WATERSOURCE;
3086 //n->setLight(LIGHTBANK_DAY, light);
3088 // Add to transforming liquid queue (in case it'd
3090 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3091 data->transforming_liquid.push_back(p);
3095 data->vmanip.m_area.add_y(em, i, -1);
3107 /***********************
3109 ************************/
3113 //TimeTaker timer1("convert mud to sand");
3119 //s16 mud_add_amount = myrand_range(2, 4);
3120 //s16 mud_add_amount = 0;
3122 /*for(s16 x=0; x<data->sectorpos_base_size*MAP_BLOCKSIZE; x++)
3123 for(s16 z=0; z<data->sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3124 for(s16 x=0-data->max_spread_amount+1;
3125 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3127 for(s16 z=0-data->max_spread_amount+1;
3128 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3131 // Node position in 2d
3132 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3134 // Determine whether to have sand here
3135 double sandnoise = noise2d_perlin(
3136 0.5+(float)p2d.X/500, 0.5+(float)p2d.Y/500,
3137 data->seed+59420, 3, 0.50);
3139 bool have_sand = (sandnoise > -0.15);
3141 if(have_sand == false)
3144 // Determine whether to have clay in the sand here
3145 double claynoise = noise2d_perlin(
3146 0.5+(float)p2d.X/500, 0.5+(float)p2d.Y/500,
3147 data->seed+4321, 6, 0.95);
3149 bool have_clay = have_sand && (claynoise > 1.25);
3151 // Find ground level
3152 s16 surface_y = find_ground_level_clever(data->vmanip, p2d);
3154 if(surface_y > WATER_LEVEL + 2)
3158 v3s16 em = data->vmanip.m_area.getExtent();
3159 s16 y_start = surface_y;
3160 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3161 u32 not_sand_counter = 0;
3162 for(s16 y=y_start; y>=y_nodes_min; y--)
3164 MapNode *n = &data->vmanip.m_data[i];
3165 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
3167 if(have_clay && (surface_y == WATER_LEVEL))
3168 n->d = CONTENT_CLAY;
3170 n->d = CONTENT_SAND;
3175 if(not_sand_counter > 3)
3179 data->vmanip.m_area.add_y(em, i, -1);
3191 //TimeTaker timer1("generate trees");
3197 // Divide area into parts
3199 s16 sidelen = data->sectorpos_base_size*MAP_BLOCKSIZE / div;
3200 double area = sidelen * sidelen;
3201 for(s16 x0=0; x0<div; x0++)
3202 for(s16 z0=0; z0<div; z0++)
3204 // Center position of part of division
3206 data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen/2 + sidelen*x0,
3207 data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen/2 + sidelen*z0
3209 // Minimum edge of part of division
3211 data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen*x0,
3212 data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen*z0
3214 // Maximum edge of part of division
3216 data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen + sidelen*x0 - 1,
3217 data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen + sidelen*z0 - 1
3220 u32 tree_count = area * tree_amount_2d(data->seed, p2d_center);
3221 // Put trees in random places on part of division
3222 for(u32 i=0; i<tree_count; i++)
3224 s16 x = myrand_range(p2d_min.X, p2d_max.X);
3225 s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
3226 s16 y = find_ground_level(data->vmanip, v2s16(x,z));
3227 // Don't make a tree under water level
3230 // Don't make a tree so high that it doesn't fit
3231 if(y > y_nodes_max - 6)
3235 u32 i = data->vmanip.m_area.index(v3s16(p));
3236 MapNode *n = &data->vmanip.m_data[i];
3237 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS && n->d != CONTENT_SAND)
3239 // Trees grow only on mud and grass
3240 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
3243 make_tree(data->vmanip, p);
3245 // Cactii grow only on sand
3246 if(n->d == CONTENT_SAND)
3249 make_cactus(data->vmanip, p);
3254 /*u32 tree_max = relative_area / 60;
3255 //u32 count = myrand_range(0, tree_max);
3256 for(u32 i=0; i<count; i++)
3258 s16 x = myrand_range(0, data->sectorpos_base_size*MAP_BLOCKSIZE-1);
3259 s16 z = myrand_range(0, data->sectorpos_base_size*MAP_BLOCKSIZE-1);
3260 x += data->sectorpos_base.X*MAP_BLOCKSIZE;
3261 z += data->sectorpos_base.Y*MAP_BLOCKSIZE;
3262 s16 y = find_ground_level(data->vmanip, v2s16(x,z));
3263 // Don't make a tree under water level
3268 make_tree(data->vmanip, p);
3278 //TimeTaker timer1("grow grass");
3284 /*for(s16 x=0-4; x<data->sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
3285 for(s16 z=0-4; z<data->sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
3286 for(s16 x=0-data->max_spread_amount;
3287 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3289 for(s16 z=0-data->max_spread_amount;
3290 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3293 // Node position in 2d
3294 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3297 Find the lowest surface to which enough light ends up
3300 Basically just wait until not air and not leaves.
3304 v3s16 em = data->vmanip.m_area.getExtent();
3305 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
3307 // Go to ground level
3308 for(y=y_nodes_max; y>=y_nodes_min; y--)
3310 MapNode &n = data->vmanip.m_data[i];
3311 if(n.d != CONTENT_AIR
3312 && n.d != CONTENT_LEAVES)
3314 data->vmanip.m_area.add_y(em, i, -1);
3316 if(y >= y_nodes_min)
3319 surface_y = y_nodes_min;
3322 u32 i = data->vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
3323 MapNode *n = &data->vmanip.m_data[i];
3324 if(n->d == CONTENT_MUD)
3325 n->d = CONTENT_GRASS;
3332 Initial lighting (sunlight)
3335 core::map<v3s16, bool> light_sources;
3338 // 750ms @cs=8, can't optimize more
3339 TimeTaker timer1("initial lighting");
3341 // NOTE: This is no used... umm... for some reason!
3344 Go through the edges and add all nodes that have light to light_sources
3348 for(s16 i=0; i<4; i++)
3350 for(s16 j=lighting_min_d;
3357 if(i == 0 || i == 1)
3359 x = (i==0) ? lighting_min_d : lighting_max_d;
3368 z = (i==0) ? lighting_min_d : lighting_max_d;
3375 // Node position in 2d
3376 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3379 v3s16 em = data->vmanip.m_area.getExtent();
3380 s16 y_start = y_nodes_max;
3381 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3382 for(s16 y=y_start; y>=y_nodes_min; y--)
3384 MapNode *n = &data->vmanip.m_data[i];
3385 if(n->getLight(LIGHTBANK_DAY) != 0)
3387 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3389 //NOTE: This is broken, at least the index has to
3398 Go through the edges and apply sunlight to them, not caring
3403 for(s16 i=0; i<4; i++)
3405 for(s16 j=lighting_min_d;
3412 if(i == 0 || i == 1)
3414 x = (i==0) ? lighting_min_d : lighting_max_d;
3423 z = (i==0) ? lighting_min_d : lighting_max_d;
3430 // Node position in 2d
3431 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3433 // Loop from top to down
3435 u8 light = LIGHT_SUN;
3436 v3s16 em = data->vmanip.m_area.getExtent();
3437 s16 y_start = y_nodes_max;
3438 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3439 for(s16 y=y_start; y>=y_nodes_min; y--)
3441 MapNode *n = &data->vmanip.m_data[i];
3442 if(light_propagates_content(n->d) == false)
3446 else if(light != LIGHT_SUN
3447 || sunlight_propagates_content(n->d) == false)
3453 n->setLight(LIGHTBANK_DAY, light);
3454 n->setLight(LIGHTBANK_NIGHT, 0);
3458 // Insert light source
3459 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3462 // Increment index by y
3463 data->vmanip.m_area.add_y(em, i, -1);
3469 /*for(s16 x=0; x<data->sectorpos_base_size*MAP_BLOCKSIZE; x++)
3470 for(s16 z=0; z<data->sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3471 /*for(s16 x=0-data->max_spread_amount+1;
3472 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3474 for(s16 z=0-data->max_spread_amount+1;
3475 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3479 This has to be 1 smaller than the actual area, because
3480 neighboring nodes are checked.
3482 for(s16 x=lighting_min_d+1;
3483 x<=lighting_max_d-1;
3485 for(s16 z=lighting_min_d+1;
3486 z<=lighting_max_d-1;
3489 // Node position in 2d
3490 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3493 Apply initial sunlight
3496 u8 light = LIGHT_SUN;
3497 bool add_to_sources = false;
3498 v3s16 em = data->vmanip.m_area.getExtent();
3499 s16 y_start = y_nodes_max;
3500 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3501 for(s16 y=y_start; y>=y_nodes_min; y--)
3503 MapNode *n = &data->vmanip.m_data[i];
3505 if(light_propagates_content(n->d) == false)
3509 else if(light != LIGHT_SUN
3510 || sunlight_propagates_content(n->d) == false)
3516 // This doesn't take much time
3517 if(add_to_sources == false)
3520 Check sides. If side is not air or water, start
3521 adding to light_sources.
3524 v3s16(0,0,1), // back
3525 v3s16(1,0,0), // right
3526 v3s16(0,0,-1), // front
3527 v3s16(-1,0,0), // left
3529 for(u32 di=0; di<4; di++)
3531 v3s16 dirp = dirs4[di];
3533 data->vmanip.m_area.add_p(em, i2, dirp);
3534 MapNode *n2 = &data->vmanip.m_data[i2];
3536 n2->d != CONTENT_AIR
3537 && n2->d != CONTENT_WATERSOURCE
3538 && n2->d != CONTENT_WATER
3540 add_to_sources = true;
3546 n->setLight(LIGHTBANK_DAY, light);
3547 n->setLight(LIGHTBANK_NIGHT, 0);
3549 // This doesn't take much time
3550 if(light != 0 && add_to_sources)
3552 // Insert light source
3553 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3556 // Increment index by y
3557 data->vmanip.m_area.add_y(em, i, -1);
3565 // Spread light around
3567 TimeTaker timer("makeChunk() spreadLight");
3568 data->vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
3575 timer_generate.stop();
3578 //###################################################################
3579 //###################################################################
3580 //###################################################################
3581 //###################################################################
3582 //###################################################################
3583 //###################################################################
3584 //###################################################################
3585 //###################################################################
3586 //###################################################################
3587 //###################################################################
3588 //###################################################################
3589 //###################################################################
3590 //###################################################################
3591 //###################################################################
3592 //###################################################################
3594 void ServerMap::initChunkMake(ChunkMakeData &data, v2s16 chunkpos)
3596 if(m_chunksize == 0)
3604 // The distance how far into the neighbors the generator is allowed to go.
3605 s16 max_spread_amount_sectors = 2;
3606 assert(max_spread_amount_sectors <= m_chunksize);
3607 s16 max_spread_amount = max_spread_amount_sectors * MAP_BLOCKSIZE;
3609 s16 y_blocks_min = -4;
3610 s16 y_blocks_max = 3;
3612 v2s16 sectorpos_base = chunk_to_sector(chunkpos);
3613 s16 sectorpos_base_size = m_chunksize;
3615 v2s16 sectorpos_bigbase =
3616 sectorpos_base - v2s16(1,1) * max_spread_amount_sectors;
3617 s16 sectorpos_bigbase_size =
3618 sectorpos_base_size + 2 * max_spread_amount_sectors;
3621 const s16 limit = MAP_GENERATION_LIMIT / MAP_BLOCKSIZE;
3622 if(sectorpos_bigbase.X < -limit
3623 || sectorpos_bigbase.X + sectorpos_bigbase_size >= limit
3624 || sectorpos_bigbase.Y < -limit
3625 || sectorpos_bigbase.Y + sectorpos_bigbase_size >= limit)
3632 data.chunkpos = chunkpos;
3633 data.y_blocks_min = y_blocks_min;
3634 data.y_blocks_max = y_blocks_max;
3635 data.sectorpos_base = sectorpos_base;
3636 data.sectorpos_base_size = sectorpos_base_size;
3637 data.sectorpos_bigbase = sectorpos_bigbase;
3638 data.sectorpos_bigbase_size = sectorpos_bigbase_size;
3639 data.max_spread_amount = max_spread_amount;
3642 Create the whole area of this and the neighboring chunks
3645 TimeTaker timer("initChunkMake() create area");
3647 for(s16 x=0; x<sectorpos_bigbase_size; x++)
3648 for(s16 z=0; z<sectorpos_bigbase_size; z++)
3650 v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
3651 ServerMapSector *sector = createSector(sectorpos);
3654 for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
3656 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
3657 MapBlock *block = createBlock(blockpos);
3659 // Lighting won't be calculated
3660 //block->setLightingExpired(true);
3661 // Lighting will be calculated
3662 block->setLightingExpired(false);
3665 Block gets sunlight if this is true.
3667 This should be set to true when the top side of a block
3668 is completely exposed to the sky.
3670 Actually this doesn't matter now because the
3671 initial lighting is done here.
3673 block->setIsUnderground(y != y_blocks_max);
3679 Now we have a big empty area.
3681 Make a ManualMapVoxelManipulator that contains this and the
3685 v3s16 bigarea_blocks_min(
3686 sectorpos_bigbase.X,
3690 v3s16 bigarea_blocks_max(
3691 sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
3693 sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
3696 data.vmanip.setMap(this);
3699 TimeTaker timer("initChunkMake() initialEmerge");
3700 data.vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
3705 MapChunk* ServerMap::finishChunkMake(ChunkMakeData &data,
3706 core::map<v3s16, MapBlock*> &changed_blocks)
3712 Blit generated stuff to map
3716 //TimeTaker timer("generateChunkRaw() blitBackAll");
3717 data.vmanip.blitBackAll(&changed_blocks);
3721 Update day/night difference cache of the MapBlocks
3724 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
3725 i.atEnd() == false; i++)
3727 MapBlock *block = i.getNode()->getValue();
3728 block->updateDayNightDiff();
3733 Copy transforming liquid information
3735 while(data.transforming_liquid.size() > 0)
3737 v3s16 p = data.transforming_liquid.pop_front();
3738 m_transforming_liquid.push_back(p);
3742 Add random objects to blocks
3745 for(s16 x=0; x<data.sectorpos_base_size; x++)
3746 for(s16 z=0; z<data.sectorpos_base_size; z++)
3748 v2s16 sectorpos = data.sectorpos_base + v2s16(x,z);
3749 ServerMapSector *sector = createSector(sectorpos);
3752 for(s16 y=data.y_blocks_min; y<=data.y_blocks_max; y++)
3754 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
3755 MapBlock *block = createBlock(blockpos);
3756 addRandomObjects(block);
3762 Create chunk metadata
3765 for(s16 x=-1; x<=1; x++)
3766 for(s16 y=-1; y<=1; y++)
3768 v2s16 chunkpos0 = data.chunkpos + v2s16(x,y);
3769 // Add chunk meta information
3770 MapChunk *chunk = getChunk(chunkpos0);
3773 chunk = new MapChunk();
3774 m_chunks.insert(chunkpos0, chunk);
3776 //chunk->setIsVolatile(true);
3777 if(chunk->getGenLevel() > GENERATED_PARTLY)
3778 chunk->setGenLevel(GENERATED_PARTLY);
3782 Set central chunk non-volatile
3784 MapChunk *chunk = getChunk(data.chunkpos);
3787 //chunk->setIsVolatile(false);
3788 chunk->setGenLevel(GENERATED_FULLY);
3791 Save changed parts of map
3800 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
3801 core::map<v3s16, MapBlock*> &changed_blocks,
3804 DSTACK(__FUNCTION_NAME);
3807 Don't generate if already fully generated
3811 MapChunk *chunk = getChunk(chunkpos);
3812 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3814 dstream<<"generateChunkRaw(): Chunk "
3815 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
3816 <<" already generated"<<std::endl;
3821 dstream<<"generateChunkRaw(): Generating chunk "
3822 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
3825 TimeTaker timer("generateChunkRaw()");
3829 // Initialize generation
3830 initChunkMake(data, chunkpos);
3835 // Finalize generation
3836 MapChunk *chunk = finishChunkMake(data, changed_blocks);
3839 Return central chunk (which was requested)
3845 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
3846 core::map<v3s16, MapBlock*> &changed_blocks)
3848 dstream<<"generateChunk(): Generating chunk "
3849 <<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
3852 /*for(s16 x=-1; x<=1; x++)
3853 for(s16 y=-1; y<=1; y++)*/
3854 for(s16 x=-0; x<=0; x++)
3855 for(s16 y=-0; y<=0; y++)
3857 v2s16 chunkpos0 = chunkpos1 + v2s16(x,y);
3858 MapChunk *chunk = getChunk(chunkpos0);
3859 // Skip if already generated
3860 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3862 generateChunkRaw(chunkpos0, changed_blocks);
3865 assert(chunkNonVolatile(chunkpos1));
3867 MapChunk *chunk = getChunk(chunkpos1);
3872 ServerMapSector * ServerMap::createSector(v2s16 p2d)
3874 DSTACKF("%s: p2d=(%d,%d)",
3879 Check if it exists already in memory
3881 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3886 Try to load it from disk (with blocks)
3888 if(loadSectorFull(p2d) == true)
3890 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3893 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
3894 throw InvalidPositionException("");
3900 Do not create over-limit
3902 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3903 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3904 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3905 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3906 throw InvalidPositionException("createSector(): pos. over limit");
3909 Generate blank sector
3912 sector = new ServerMapSector(this, p2d);
3914 // Sector position on map in nodes
3915 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3920 m_sectors.insert(p2d, sector);
3926 MapSector * ServerMap::emergeSector(v2s16 p2d,
3927 core::map<v3s16, MapBlock*> &changed_blocks)
3929 DSTACK("%s: p2d=(%d,%d)",
3936 v2s16 chunkpos = sector_to_chunk(p2d);
3937 /*bool chunk_nonvolatile = false;
3938 MapChunk *chunk = getChunk(chunkpos);
3939 if(chunk && chunk->getIsVolatile() == false)
3940 chunk_nonvolatile = true;*/
3941 bool chunk_nonvolatile = chunkNonVolatile(chunkpos);
3944 If chunk is not fully generated, generate chunk
3946 if(chunk_nonvolatile == false)
3948 // Generate chunk and neighbors
3949 generateChunk(chunkpos, changed_blocks);
3953 Return sector if it exists now
3955 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3960 Try to load it from disk
3962 if(loadSectorFull(p2d) == true)
3964 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3967 dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
3968 throw InvalidPositionException("");
3974 generateChunk should have generated the sector
3978 dstream<<"WARNING: ServerMap::emergeSector: Cannot find sector ("
3979 <<p2d.X<<","<<p2d.Y<<" and chunk is already generated. "
3983 dstream<<"WARNING: Creating an empty sector."<<std::endl;
3985 return createSector(p2d);
3990 dstream<<"WARNING: Forcing regeneration of chunk."<<std::endl;
3993 generateChunkRaw(chunkpos, changed_blocks, true);
3996 Return sector if it exists now
3998 sector = getSectorNoGenerateNoEx(p2d);
4002 dstream<<"ERROR: Could not get sector from anywhere."<<std::endl;
4010 //return generateSector();
4015 NOTE: This is not used for main map generation, only for blocks
4016 that are very high or low
4018 MapBlock * ServerMap::generateBlock(
4020 MapBlock *original_dummy,
4021 ServerMapSector *sector,
4022 core::map<v3s16, MapBlock*> &changed_blocks,
4023 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4026 DSTACKF("%s: p=(%d,%d,%d)",
4030 // If chunks are disabled
4031 /*if(m_chunksize == 0)
4033 dstream<<"ServerMap::generateBlock(): Chunks disabled -> "
4034 <<"not generating."<<std::endl;
4038 /*dstream<<"generateBlock(): "
4039 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4042 MapBlock *block = original_dummy;
4044 v2s16 p2d(p.X, p.Z);
4046 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
4049 Do not generate over-limit
4051 if(blockpos_over_limit(p))
4053 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
4054 throw InvalidPositionException("generateBlock(): pos. over limit");
4058 If block doesn't exist, create one.
4059 If it exists, it is a dummy. In that case unDummify() it.
4061 NOTE: This already sets the map as the parent of the block
4065 block = sector->createBlankBlockNoInsert(block_y);
4069 // Remove the block so that nobody can get a half-generated one.
4070 sector->removeBlock(block);
4071 // Allocate the block to contain the generated data
4077 Generate a completely empty block
4079 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4080 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4082 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4086 block->setNode(v3s16(x0,y0,z0), n);
4091 Generate a proper block
4094 u8 water_material = CONTENT_WATERSOURCE;
4096 s32 lowest_ground_y = 32767;
4097 s32 highest_ground_y = -32768;
4099 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4100 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4102 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
4104 //s16 surface_y = 0;
4106 s16 mud_add_amount = get_mud_add_amount(m_seed, p2d_nodes+v2s16(x0,z0));
4108 s16 surface_y = base_rock_level_2d(m_seed, p2d_nodes+v2s16(x0,z0))
4110 // If chunks are disabled
4111 if(m_chunksize == 0)
4112 surface_y = WATER_LEVEL + 1;
4114 if(surface_y < lowest_ground_y)
4115 lowest_ground_y = surface_y;
4116 if(surface_y > highest_ground_y)
4117 highest_ground_y = surface_y;
4119 s32 surface_depth = AVERAGE_MUD_AMOUNT;
4121 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4123 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
4128 NOTE: If there are some man-made structures above the
4129 newly created block, they won't be taken into account.
4131 if(real_y > surface_y)
4132 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
4138 // If node is over heightmap y, it's air or water
4139 if(real_y > surface_y)
4141 // If under water level, it's water
4142 if(real_y < WATER_LEVEL)
4144 n.d = water_material;
4145 n.setLight(LIGHTBANK_DAY,
4146 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
4148 Add to transforming liquid queue (in case it'd
4151 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
4152 m_transforming_liquid.push_back(real_pos);
4158 // Else it's ground or caves (air)
4161 // If it's surface_depth under ground, it's stone
4162 if(real_y <= surface_y - surface_depth)
4164 n.d = CONTENT_STONE;
4168 // It is mud if it is under the first ground
4169 // level or under water
4170 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
4176 n.d = CONTENT_GRASS;
4179 //n.d = CONTENT_MUD;
4181 /*// If under water level, it's mud
4182 if(real_y < WATER_LEVEL)
4184 // Only the topmost node is grass
4185 else if(real_y <= surface_y - 1)
4188 n.d = CONTENT_GRASS;*/
4192 block->setNode(v3s16(x0,y0,z0), n);
4197 Calculate some helper variables
4200 // Completely underground if the highest part of block is under lowest
4202 // This has to be very sure; it's probably one too strict now but
4203 // that's just better.
4204 bool completely_underground =
4205 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
4207 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
4209 bool mostly_underwater_surface = false;
4210 if(highest_ground_y < WATER_LEVEL
4211 && some_part_underground && !completely_underground)
4212 mostly_underwater_surface = true;
4215 Get local attributes
4218 //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
4220 float caves_amount = 0.5;
4225 NOTE: BEWARE: Too big amount of attribute points slows verything
4227 1 interpolation from 5000 points takes 2-3ms.
4229 //TimeTaker timer("generateBlock() local attribute retrieval");
4230 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
4231 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
4232 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
4236 //dstream<<"generateBlock(): Done"<<std::endl;
4242 // Initialize temporary table
4243 const s32 ued = MAP_BLOCKSIZE;
4244 bool underground_emptiness[ued*ued*ued];
4245 for(s32 i=0; i<ued*ued*ued; i++)
4247 underground_emptiness[i] = 0;
4254 Initialize orp and ors. Try to find if some neighboring
4255 MapBlock has a tunnel ended in its side
4259 (float)(myrand()%ued)+0.5,
4260 (float)(myrand()%ued)+0.5,
4261 (float)(myrand()%ued)+0.5
4264 bool found_existing = false;
4270 for(s16 y=0; y<ued; y++)
4271 for(s16 x=0; x<ued; x++)
4273 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4274 if(getNode(ap).d == CONTENT_AIR)
4276 orp = v3f(x+1,y+1,0);
4277 found_existing = true;
4278 goto continue_generating;
4282 catch(InvalidPositionException &e){}
4288 for(s16 y=0; y<ued; y++)
4289 for(s16 x=0; x<ued; x++)
4291 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4292 if(getNode(ap).d == CONTENT_AIR)
4294 orp = v3f(x+1,y+1,ued-1);
4295 found_existing = true;
4296 goto continue_generating;
4300 catch(InvalidPositionException &e){}
4306 for(s16 y=0; y<ued; y++)
4307 for(s16 z=0; z<ued; z++)
4309 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4310 if(getNode(ap).d == CONTENT_AIR)
4312 orp = v3f(0,y+1,z+1);
4313 found_existing = true;
4314 goto continue_generating;
4318 catch(InvalidPositionException &e){}
4324 for(s16 y=0; y<ued; y++)
4325 for(s16 z=0; z<ued; z++)
4327 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4328 if(getNode(ap).d == CONTENT_AIR)
4330 orp = v3f(ued-1,y+1,z+1);
4331 found_existing = true;
4332 goto continue_generating;
4336 catch(InvalidPositionException &e){}
4342 for(s16 x=0; x<ued; x++)
4343 for(s16 z=0; z<ued; z++)
4345 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4346 if(getNode(ap).d == CONTENT_AIR)
4348 orp = v3f(x+1,0,z+1);
4349 found_existing = true;
4350 goto continue_generating;
4354 catch(InvalidPositionException &e){}
4360 for(s16 x=0; x<ued; x++)
4361 for(s16 z=0; z<ued; z++)
4363 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4364 if(getNode(ap).d == CONTENT_AIR)
4366 orp = v3f(x+1,ued-1,z+1);
4367 found_existing = true;
4368 goto continue_generating;
4372 catch(InvalidPositionException &e){}
4374 continue_generating:
4377 Choose whether to actually generate cave
4379 bool do_generate_caves = true;
4380 // Don't generate if no part is underground
4381 if(!some_part_underground)
4383 do_generate_caves = false;
4385 // Don't generate if mostly underwater surface
4386 /*else if(mostly_underwater_surface)
4388 do_generate_caves = false;
4390 // Partly underground = cave
4391 else if(!completely_underground)
4393 do_generate_caves = (rand() % 100 <= (s32)(caves_amount*100));
4395 // Found existing cave underground
4396 else if(found_existing && completely_underground)
4398 do_generate_caves = (rand() % 100 <= (s32)(caves_amount*100));
4400 // Underground and no caves found
4403 do_generate_caves = (rand() % 300 <= (s32)(caves_amount*100));
4406 if(do_generate_caves)
4409 Generate some tunnel starting from orp and ors
4411 for(u16 i=0; i<3; i++)
4414 (float)(myrand()%ued)+0.5,
4415 (float)(myrand()%ued)+0.5,
4416 (float)(myrand()%ued)+0.5
4420 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
4424 for(float f=0; f<1.0; f+=0.04)
4426 v3f fp = orp + vec * f;
4427 v3s16 cp(fp.X, fp.Y, fp.Z);
4429 s16 d1 = d0 + rs - 1;
4430 for(s16 z0=d0; z0<=d1; z0++)
4432 s16 si = rs - abs(z0);
4433 for(s16 x0=-si; x0<=si-1; x0++)
4435 s16 si2 = rs - abs(x0);
4436 for(s16 y0=-si2+1; y0<=si2-1; y0++)
4442 if(isInArea(p, ued) == false)
4444 underground_emptiness[ued*ued*z + ued*y + x] = 1;
4456 // Set to true if has caves.
4457 // Set when some non-air is changed to air when making caves.
4458 bool has_caves = false;
4461 Apply temporary cave data to block
4464 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4465 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4467 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4469 MapNode n = block->getNode(v3s16(x0,y0,z0));
4472 if(underground_emptiness[
4473 ued*ued*(z0*ued/MAP_BLOCKSIZE)
4474 +ued*(y0*ued/MAP_BLOCKSIZE)
4475 +(x0*ued/MAP_BLOCKSIZE)])
4477 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
4486 block->setNode(v3s16(x0,y0,z0), n);
4491 This is used for guessing whether or not the block should
4492 receive sunlight from the top if the block above doesn't exist
4494 block->setIsUnderground(completely_underground);
4497 Force lighting update if some part of block is partly
4498 underground and has caves.
4500 /*if(some_part_underground && !completely_underground && has_caves)
4502 //dstream<<"Half-ground caves"<<std::endl;
4503 lighting_invalidated_blocks[block->getPos()] = block;
4506 // DEBUG: Always update lighting
4507 //lighting_invalidated_blocks[block->getPos()] = block;
4513 if(some_part_underground)
4515 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
4520 for(s16 i=0; i<underground_level/4 + 1; i++)
4522 if(myrand()%50 == 0)
4525 (myrand()%(MAP_BLOCKSIZE-2))+1,
4526 (myrand()%(MAP_BLOCKSIZE-2))+1,
4527 (myrand()%(MAP_BLOCKSIZE-2))+1
4533 for(u16 i=0; i<27; i++)
4535 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4537 block->setNode(cp+g_27dirs[i], n);
4545 u16 coal_amount = 30;
4546 u16 coal_rareness = 60 / coal_amount;
4547 if(coal_rareness == 0)
4549 if(myrand()%coal_rareness == 0)
4551 u16 a = myrand() % 16;
4552 u16 amount = coal_amount * a*a*a / 1000;
4553 for(s16 i=0; i<amount; i++)
4556 (myrand()%(MAP_BLOCKSIZE-2))+1,
4557 (myrand()%(MAP_BLOCKSIZE-2))+1,
4558 (myrand()%(MAP_BLOCKSIZE-2))+1
4562 n.d = CONTENT_STONE;
4563 n.param = MINERAL_COAL;
4565 for(u16 i=0; i<27; i++)
4567 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4569 block->setNode(cp+g_27dirs[i], n);
4577 //TODO: change to iron_amount or whatever
4578 u16 iron_amount = 15;
4579 u16 iron_rareness = 60 / iron_amount;
4580 if(iron_rareness == 0)
4582 if(myrand()%iron_rareness == 0)
4584 u16 a = myrand() % 16;
4585 u16 amount = iron_amount * a*a*a / 1000;
4586 for(s16 i=0; i<amount; i++)
4589 (myrand()%(MAP_BLOCKSIZE-2))+1,
4590 (myrand()%(MAP_BLOCKSIZE-2))+1,
4591 (myrand()%(MAP_BLOCKSIZE-2))+1
4595 n.d = CONTENT_STONE;
4596 n.param = MINERAL_IRON;
4598 for(u16 i=0; i<27; i++)
4600 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4602 block->setNode(cp+g_27dirs[i], n);
4609 Create a few rats in empty blocks underground
4611 if(completely_underground)
4613 //for(u16 i=0; i<2; i++)
4616 (myrand()%(MAP_BLOCKSIZE-2))+1,
4617 (myrand()%(MAP_BLOCKSIZE-2))+1,
4618 (myrand()%(MAP_BLOCKSIZE-2))+1
4621 // Check that the place is empty
4622 //if(!is_ground_content(block->getNode(cp).d))
4625 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp, BS));
4626 block->addObject(obj);
4631 #endif // end of proper block generation
4634 Add block to sector.
4636 sector->insertBlock(block);
4638 // Lighting is invalid after generation.
4639 block->setLightingExpired(true);
4646 <<"lighting_invalidated_blocks.size()"
4650 <<" "<<lighting_invalidated_blocks.size()
4652 <<", "<<completely_underground
4653 <<", "<<some_part_underground
4660 MapBlock * ServerMap::createBlock(v3s16 p)
4662 DSTACKF("%s: p=(%d,%d,%d)",
4663 __FUNCTION_NAME, p.X, p.Y, p.Z);
4666 Do not create over-limit
4668 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4669 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4670 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4671 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4672 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4673 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4674 throw InvalidPositionException("createBlock(): pos. over limit");
4676 v2s16 p2d(p.X, p.Z);
4679 This will create or load a sector if not found in memory.
4680 If block exists on disk, it will be loaded.
4682 NOTE: On old save formats, this will be slow, as it generates
4683 lighting on blocks for them.
4685 ServerMapSector *sector;
4687 sector = (ServerMapSector*)createSector(p2d);
4688 assert(sector->getId() == MAPSECTOR_SERVER);
4690 catch(InvalidPositionException &e)
4692 dstream<<"createBlock: createSector() failed"<<std::endl;
4696 NOTE: This should not be done, or at least the exception
4697 should not be passed on as std::exception, because it
4698 won't be catched at all.
4700 /*catch(std::exception &e)
4702 dstream<<"createBlock: createSector() failed: "
4703 <<e.what()<<std::endl;
4708 Try to get a block from the sector
4711 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4715 block = sector->createBlankBlock(block_y);
4719 MapBlock * ServerMap::emergeBlock(
4721 bool only_from_disk,
4722 core::map<v3s16, MapBlock*> &changed_blocks,
4723 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4726 DSTACKF("%s: p=(%d,%d,%d), only_from_disk=%d",
4728 p.X, p.Y, p.Z, only_from_disk);
4731 Do not generate over-limit
4733 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4734 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4735 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4736 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4737 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4738 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4739 throw InvalidPositionException("emergeBlock(): pos. over limit");
4741 v2s16 p2d(p.X, p.Z);
4744 This will create or load a sector if not found in memory.
4745 If block exists on disk, it will be loaded.
4747 ServerMapSector *sector;
4749 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
4750 assert(sector->getId() == MAPSECTOR_SERVER);
4752 catch(InvalidPositionException &e)
4754 dstream<<"emergeBlock: emergeSector() failed: "
4755 <<e.what()<<std::endl;
4756 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4758 <<"You could try to delete it."<<std::endl;
4761 catch(VersionMismatchException &e)
4763 dstream<<"emergeBlock: emergeSector() failed: "
4764 <<e.what()<<std::endl;
4765 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4767 <<"You could try to delete it."<<std::endl;
4771 NOTE: This should not be done, or at least the exception
4772 should not be passed on as std::exception, because it
4773 won't be catched at all.
4775 /*catch(std::exception &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;
4786 Try to get a block from the sector
4789 bool does_not_exist = false;
4790 bool lighting_expired = false;
4791 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4795 does_not_exist = true;
4797 else if(block->isDummy() == true)
4799 does_not_exist = true;
4801 else if(block->getLightingExpired())
4803 lighting_expired = true;
4808 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
4813 If block was not found on disk and not going to generate a
4814 new one, make sure there is a dummy block in place.
4816 if(only_from_disk && (does_not_exist || lighting_expired))
4818 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
4822 // Create dummy block
4823 block = new MapBlock(this, p, true);
4825 // Add block to sector
4826 sector->insertBlock(block);
4832 //dstream<<"Not found on disk, generating."<<std::endl;
4834 //TimeTaker("emergeBlock() generate");
4836 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
4839 If the block doesn't exist, generate the block.
4843 block = generateBlock(p, block, sector, changed_blocks,
4844 lighting_invalidated_blocks);
4847 if(lighting_expired)
4849 lighting_invalidated_blocks.insert(p, block);
4853 Initially update sunlight
4857 core::map<v3s16, bool> light_sources;
4858 bool black_air_left = false;
4859 bool bottom_invalid =
4860 block->propagateSunlight(light_sources, true,
4861 &black_air_left, true);
4863 // If sunlight didn't reach everywhere and part of block is
4864 // above ground, lighting has to be properly updated
4865 //if(black_air_left && some_part_underground)
4868 lighting_invalidated_blocks[block->getPos()] = block;
4873 lighting_invalidated_blocks[block->getPos()] = block;
4880 s16 ServerMap::findGroundLevel(v2s16 p2d)
4883 Uh, just do something random...
4885 // Find existing map from top to down
4888 v3s16 p(p2d.X, max, p2d.Y);
4889 for(; p.Y>min; p.Y--)
4891 MapNode n = getNodeNoEx(p);
4892 if(n.d != CONTENT_IGNORE)
4897 // If this node is not air, go to plan b
4898 if(getNodeNoEx(p).d != CONTENT_AIR)
4900 // Search existing walkable and return it
4901 for(; p.Y>min; p.Y--)
4903 MapNode n = getNodeNoEx(p);
4904 if(content_walkable(n.d) && n.d != CONTENT_IGNORE)
4910 Plan B: Get from map generator perlin noise function
4912 // This won't work if proper generation is disabled
4913 if(m_chunksize == 0)
4914 return WATER_LEVEL+2;
4915 double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
4919 void ServerMap::createDirs(std::string path)
4921 if(fs::CreateAllDirs(path) == false)
4923 m_dout<<DTIME<<"ServerMap: Failed to create directory "
4924 <<"\""<<path<<"\""<<std::endl;
4925 throw BaseException("ServerMap failed to create directory");
4929 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
4935 snprintf(cc, 9, "%.4x%.4x",
4936 (unsigned int)pos.X&0xffff,
4937 (unsigned int)pos.Y&0xffff);
4939 return m_savedir + "/sectors/" + cc;
4941 snprintf(cc, 9, "%.3x/%.3x",
4942 (unsigned int)pos.X&0xfff,
4943 (unsigned int)pos.Y&0xfff);
4945 return m_savedir + "/sectors2/" + cc;
4951 v2s16 ServerMap::getSectorPos(std::string dirname)
4955 size_t spos = dirname.rfind('/') + 1;
4956 assert(spos != std::string::npos);
4957 if(dirname.size() - spos == 8)
4960 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
4962 else if(dirname.size() - spos == 3)
4965 r = sscanf(dirname.substr(spos-4).c_str(), "%3x/%3x", &x, &y);
4966 // Sign-extend the 12 bit values up to 16 bits...
4967 if(x&0x800) x|=0xF000;
4968 if(y&0x800) y|=0xF000;
4975 v2s16 pos((s16)x, (s16)y);
4979 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
4981 v2s16 p2d = getSectorPos(sectordir);
4983 if(blockfile.size() != 4){
4984 throw InvalidFilenameException("Invalid block filename");
4987 int r = sscanf(blockfile.c_str(), "%4x", &y);
4989 throw InvalidFilenameException("Invalid block filename");
4990 return v3s16(p2d.X, y, p2d.Y);
4993 void ServerMap::save(bool only_changed)
4995 DSTACK(__FUNCTION_NAME);
4996 if(m_map_saving_enabled == false)
4998 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
5002 if(only_changed == false)
5003 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
5006 if(only_changed == false || m_map_metadata_changed)
5011 // Disable saving chunk metadata if chunks are disabled
5012 if(m_chunksize != 0)
5014 if(only_changed == false || anyChunkModified())
5018 u32 sector_meta_count = 0;
5019 u32 block_count = 0;
5022 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5024 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
5025 for(; i.atEnd() == false; i++)
5027 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
5028 assert(sector->getId() == MAPSECTOR_SERVER);
5030 if(sector->differs_from_disk || only_changed == false)
5032 saveSectorMeta(sector);
5033 sector_meta_count++;
5035 core::list<MapBlock*> blocks;
5036 sector->getBlocks(blocks);
5037 core::list<MapBlock*>::Iterator j;
5038 for(j=blocks.begin(); j!=blocks.end(); j++)
5040 MapBlock *block = *j;
5041 if(block->getChangedFlag() || only_changed == false)
5046 /*dstream<<"ServerMap: Written block ("
5047 <<block->getPos().X<<","
5048 <<block->getPos().Y<<","
5049 <<block->getPos().Z<<")"
5058 Only print if something happened or saved whole map
5060 if(only_changed == false || sector_meta_count != 0
5061 || block_count != 0)
5063 dstream<<DTIME<<"ServerMap: Written: "
5064 <<sector_meta_count<<" sector metadata files, "
5065 <<block_count<<" block files"
5071 // NOTE: Doing this is insane. Deprecated and probably broken.
5072 void ServerMap::loadAll()
5074 DSTACK(__FUNCTION_NAME);
5075 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
5080 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
5082 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
5084 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5087 s32 printed_counter = -100000;
5088 s32 count = list.size();
5090 std::vector<fs::DirListNode>::iterator i;
5091 for(i=list.begin(); i!=list.end(); i++)
5093 if(counter > printed_counter + 10)
5095 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
5096 printed_counter = counter;
5100 MapSector *sector = NULL;
5102 // We want directories
5106 sector = loadSectorMeta(i->name);
5108 catch(InvalidFilenameException &e)
5110 // This catches unknown crap in directory
5113 std::vector<fs::DirListNode> list2 = fs::GetDirListing
5114 (m_savedir+"/sectors/"+i->name);
5115 std::vector<fs::DirListNode>::iterator i2;
5116 for(i2=list2.begin(); i2!=list2.end(); i2++)
5122 loadBlock(i->name, i2->name, sector);
5124 catch(InvalidFilenameException &e)
5126 // This catches unknown crap in directory
5130 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
5135 void ServerMap::saveMasterHeightmap()
5137 DSTACK(__FUNCTION_NAME);
5139 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
5141 createDir(m_savedir);
5143 /*std::string fullpath = m_savedir + "/master_heightmap";
5144 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5145 if(o.good() == false)
5146 throw FileNotGoodException("Cannot open master heightmap");*/
5148 // Format used for writing
5149 //u8 version = SER_FMT_VER_HIGHEST;
5152 void ServerMap::loadMasterHeightmap()
5154 DSTACK(__FUNCTION_NAME);
5156 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
5158 /*std::string fullpath = m_savedir + "/master_heightmap";
5159 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5160 if(is.good() == false)
5161 throw FileNotGoodException("Cannot open master heightmap");*/
5165 void ServerMap::saveMapMeta()
5167 DSTACK(__FUNCTION_NAME);
5169 dstream<<"INFO: ServerMap::saveMapMeta(): "
5170 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
5173 createDirs(m_savedir);
5175 std::string fullpath = m_savedir + "/map_meta.txt";
5176 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
5177 if(os.good() == false)
5179 dstream<<"ERROR: ServerMap::saveMapMeta(): "
5180 <<"could not open"<<fullpath<<std::endl;
5181 throw FileNotGoodException("Cannot open chunk metadata");
5185 params.setU64("seed", m_seed);
5186 params.setS32("chunksize", m_chunksize);
5188 params.writeLines(os);
5190 os<<"[end_of_params]\n";
5192 m_map_metadata_changed = false;
5195 void ServerMap::loadMapMeta()
5197 DSTACK(__FUNCTION_NAME);
5199 dstream<<"INFO: ServerMap::loadMapMeta(): Loading map metadata"
5202 std::string fullpath = m_savedir + "/map_meta.txt";
5203 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5204 if(is.good() == false)
5206 dstream<<"ERROR: ServerMap::loadMapMeta(): "
5207 <<"could not open"<<fullpath<<std::endl;
5208 throw FileNotGoodException("Cannot open map metadata");
5216 throw SerializationError
5217 ("ServerMap::loadMapMeta(): [end_of_params] not found");
5219 std::getline(is, line);
5220 std::string trimmedline = trim(line);
5221 if(trimmedline == "[end_of_params]")
5223 params.parseConfigLine(line);
5226 m_seed = params.getU64("seed");
5227 m_chunksize = params.getS32("chunksize");
5229 dstream<<"INFO: ServerMap::loadMapMeta(): "
5230 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
5234 void ServerMap::saveChunkMeta()
5236 DSTACK(__FUNCTION_NAME);
5238 // This should not be called if chunks are disabled.
5239 assert(m_chunksize != 0);
5241 u32 count = m_chunks.size();
5243 dstream<<"INFO: ServerMap::saveChunkMeta(): Saving metadata of "
5244 <<count<<" chunks"<<std::endl;
5246 createDirs(m_savedir);
5248 std::string fullpath = m_savedir + "/chunk_meta";
5249 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
5250 if(os.good() == false)
5252 dstream<<"ERROR: ServerMap::saveChunkMeta(): "
5253 <<"could not open"<<fullpath<<std::endl;
5254 throw FileNotGoodException("Cannot open chunk metadata");
5260 os.write((char*)&version, 1);
5265 writeU32(buf, count);
5266 os.write((char*)buf, 4);
5268 for(core::map<v2s16, MapChunk*>::Iterator
5269 i = m_chunks.getIterator();
5270 i.atEnd()==false; i++)
5272 v2s16 p = i.getNode()->getKey();
5273 MapChunk *chunk = i.getNode()->getValue();
5276 os.write((char*)buf, 4);
5278 chunk->serialize(os, version);
5281 setChunksNonModified();
5284 void ServerMap::loadChunkMeta()
5286 DSTACK(__FUNCTION_NAME);
5288 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading chunk metadata"
5291 std::string fullpath = m_savedir + "/chunk_meta";
5292 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5293 if(is.good() == false)
5295 dstream<<"ERROR: ServerMap::loadChunkMeta(): "
5296 <<"could not open"<<fullpath<<std::endl;
5297 throw FileNotGoodException("Cannot open chunk metadata");
5303 is.read((char*)&version, 1);
5308 is.read((char*)buf, 4);
5309 u32 count = readU32(buf);
5311 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading metadata of "
5312 <<count<<" chunks"<<std::endl;
5314 for(u32 i=0; i<count; i++)
5317 MapChunk *chunk = new MapChunk();
5319 is.read((char*)buf, 4);
5322 chunk->deSerialize(is, version);
5323 m_chunks.insert(p, chunk);
5327 void ServerMap::saveSectorMeta(ServerMapSector *sector)
5329 DSTACK(__FUNCTION_NAME);
5330 // Format used for writing
5331 u8 version = SER_FMT_VER_HIGHEST;
5333 v2s16 pos = sector->getPos();
5334 std::string dir = getSectorDir(pos);
5337 std::string fullpath = dir + "/meta";
5338 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5339 if(o.good() == false)
5340 throw FileNotGoodException("Cannot open sector metafile");
5342 sector->serialize(o, version);
5344 sector->differs_from_disk = false;
5347 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
5349 DSTACK(__FUNCTION_NAME);
5351 v2s16 p2d = getSectorPos(sectordir);
5353 ServerMapSector *sector = NULL;
5355 std::string fullpath = sectordir + "/meta";
5356 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5357 if(is.good() == false)
5359 // If the directory exists anyway, it probably is in some old
5360 // format. Just go ahead and create the sector.
5361 if(fs::PathExists(sectordir))
5363 dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
5364 <<fullpath<<" doesn't exist but directory does."
5365 <<" Continuing with a sector with no metadata."
5367 sector = new ServerMapSector(this, p2d);
5368 m_sectors.insert(p2d, sector);
5372 throw FileNotGoodException("Cannot open sector metafile");
5377 sector = ServerMapSector::deSerialize
5378 (is, this, p2d, m_sectors);
5380 saveSectorMeta(sector);
5383 sector->differs_from_disk = false;
5388 bool ServerMap::loadSectorFull(v2s16 p2d)
5390 DSTACK(__FUNCTION_NAME);
5392 MapSector *sector = NULL;
5394 // The directory layout we're going to load from.
5395 // 1 - original sectors/xxxxzzzz/
5396 // 2 - new sectors2/xxx/zzz/
5397 // If we load from anything but the latest structure, we will
5398 // immediately save to the new one, and remove the old.
5400 std::string sectordir1 = getSectorDir(p2d, 1);
5401 std::string sectordir;
5402 if(fs::PathExists(sectordir1))
5404 sectordir = sectordir1;
5409 sectordir = getSectorDir(p2d, 2);
5412 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5415 sector = loadSectorMeta(sectordir, loadlayout != 2);
5417 catch(InvalidFilenameException &e)
5421 catch(FileNotGoodException &e)
5425 catch(std::exception &e)
5433 std::vector<fs::DirListNode> list2 = fs::GetDirListing
5435 std::vector<fs::DirListNode>::iterator i2;
5436 for(i2=list2.begin(); i2!=list2.end(); i2++)
5442 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
5444 catch(InvalidFilenameException &e)
5446 // This catches unknown crap in directory
5452 dstream<<"Sector converted to new layout - deleting "<<
5453 sectordir1<<std::endl;
5454 fs::RecursiveDelete(sectordir1);
5461 void ServerMap::saveBlock(MapBlock *block)
5463 DSTACK(__FUNCTION_NAME);
5465 Dummy blocks are not written
5467 if(block->isDummy())
5469 /*v3s16 p = block->getPos();
5470 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
5471 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
5475 // Format used for writing
5476 u8 version = SER_FMT_VER_HIGHEST;
5478 v3s16 p3d = block->getPos();
5479 v2s16 p2d(p3d.X, p3d.Z);
5480 std::string dir = getSectorDir(p2d);
5484 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
5485 std::string fullpath = dir + "/" + cc;
5486 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5487 if(o.good() == false)
5488 throw FileNotGoodException("Cannot open block data");
5491 [0] u8 serialization version
5494 o.write((char*)&version, 1);
5497 block->serialize(o, version);
5499 // Write extra data stored on disk
5500 block->serializeDiskExtra(o, version);
5502 // We just wrote it to the disk so clear modified flag
5503 block->resetChangedFlag();
5506 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
5508 DSTACK(__FUNCTION_NAME);
5510 std::string fullpath = sectordir+"/"+blockfile;
5513 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5514 if(is.good() == false)
5515 throw FileNotGoodException("Cannot open block file");
5517 v3s16 p3d = getBlockPos(sectordir, blockfile);
5518 v2s16 p2d(p3d.X, p3d.Z);
5520 assert(sector->getPos() == p2d);
5522 u8 version = SER_FMT_VER_INVALID;
5523 is.read((char*)&version, 1);
5526 throw SerializationError("ServerMap::loadBlock(): Failed"
5527 " to read MapBlock version");
5529 /*u32 block_size = MapBlock::serializedLength(version);
5530 SharedBuffer<u8> data(block_size);
5531 is.read((char*)*data, block_size);*/
5533 // This will always return a sector because we're the server
5534 //MapSector *sector = emergeSector(p2d);
5536 MapBlock *block = NULL;
5537 bool created_new = false;
5539 block = sector->getBlockNoCreate(p3d.Y);
5541 catch(InvalidPositionException &e)
5543 block = sector->createBlankBlockNoInsert(p3d.Y);
5548 block->deSerialize(is, version);
5550 // Read extra data stored on disk
5551 block->deSerializeDiskExtra(is, version);
5553 // If it's a new block, insert it to the map
5555 sector->insertBlock(block);
5558 Save blocks loaded in old format in new format
5561 if(version < SER_FMT_VER_HIGHEST || save_after_load)
5566 // We just loaded it from the disk, so it's up-to-date.
5567 block->resetChangedFlag();
5570 catch(SerializationError &e)
5572 dstream<<"WARNING: Invalid block data on disk "
5573 "(SerializationError). Ignoring. "
5574 "A new one will be generated."
5577 // TODO: Backup file; name is in fullpath.
5581 void ServerMap::PrintInfo(std::ostream &out)
5592 ClientMap::ClientMap(
5594 MapDrawControl &control,
5595 scene::ISceneNode* parent,
5596 scene::ISceneManager* mgr,
5600 scene::ISceneNode(parent, mgr, id),
5603 m_camera_position(0,0,0),
5604 m_camera_direction(0,0,1)
5606 m_camera_mutex.Init();
5607 assert(m_camera_mutex.IsInitialized());
5609 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
5610 BS*1000000,BS*1000000,BS*1000000);
5613 ClientMap::~ClientMap()
5615 /*JMutexAutoLock lock(mesh_mutex);
5624 MapSector * ClientMap::emergeSector(v2s16 p2d)
5626 DSTACK(__FUNCTION_NAME);
5627 // Check that it doesn't exist already
5629 return getSectorNoGenerate(p2d);
5631 catch(InvalidPositionException &e)
5636 ClientMapSector *sector = new ClientMapSector(this, p2d);
5639 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5640 m_sectors.insert(p2d, sector);
5646 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
5648 DSTACK(__FUNCTION_NAME);
5649 ClientMapSector *sector = NULL;
5651 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5653 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
5657 sector = (ClientMapSector*)n->getValue();
5658 assert(sector->getId() == MAPSECTOR_CLIENT);
5662 sector = new ClientMapSector(this, p2d);
5664 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5665 m_sectors.insert(p2d, sector);
5669 sector->deSerialize(is);
5672 void ClientMap::OnRegisterSceneNode()
5676 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5677 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5680 ISceneNode::OnRegisterSceneNode();
5683 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5685 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5686 DSTACK(__FUNCTION_NAME);
5688 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5691 Get time for measuring timeout.
5693 Measuring time is very useful for long delays when the
5694 machine is swapping a lot.
5696 int time1 = time(0);
5698 //u32 daynight_ratio = m_client->getDayNightRatio();
5700 m_camera_mutex.Lock();
5701 v3f camera_position = m_camera_position;
5702 v3f camera_direction = m_camera_direction;
5703 m_camera_mutex.Unlock();
5706 Get all blocks and draw all visible ones
5709 v3s16 cam_pos_nodes(
5710 camera_position.X / BS,
5711 camera_position.Y / BS,
5712 camera_position.Z / BS);
5714 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5716 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5717 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5719 // Take a fair amount as we will be dropping more out later
5721 p_nodes_min.X / MAP_BLOCKSIZE - 1,
5722 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
5723 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
5725 p_nodes_max.X / MAP_BLOCKSIZE + 1,
5726 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
5727 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
5729 u32 vertex_count = 0;
5731 // For limiting number of mesh updates per frame
5732 u32 mesh_update_count = 0;
5734 u32 blocks_would_have_drawn = 0;
5735 u32 blocks_drawn = 0;
5737 //NOTE: The sectors map should be locked but we're not doing it
5738 // because it'd cause too much delays
5740 int timecheck_counter = 0;
5741 core::map<v2s16, MapSector*>::Iterator si;
5742 si = m_sectors.getIterator();
5743 for(; si.atEnd() == false; si++)
5746 timecheck_counter++;
5747 if(timecheck_counter > 50)
5749 timecheck_counter = 0;
5750 int time2 = time(0);
5751 if(time2 > time1 + 4)
5753 dstream<<"ClientMap::renderMap(): "
5754 "Rendering takes ages, returning."
5761 MapSector *sector = si.getNode()->getValue();
5762 v2s16 sp = sector->getPos();
5764 if(m_control.range_all == false)
5766 if(sp.X < p_blocks_min.X
5767 || sp.X > p_blocks_max.X
5768 || sp.Y < p_blocks_min.Z
5769 || sp.Y > p_blocks_max.Z)
5773 core::list< MapBlock * > sectorblocks;
5774 sector->getBlocks(sectorblocks);
5780 core::list< MapBlock * >::Iterator i;
5781 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5783 MapBlock *block = *i;
5786 Compare block position to camera position, skip
5787 if not seen on display
5790 float range = 100000 * BS;
5791 if(m_control.range_all == false)
5792 range = m_control.wanted_range * BS;
5795 if(isBlockInSight(block->getPos(), camera_position,
5796 camera_direction, range, &d) == false)
5801 // This is ugly (spherical distance limit?)
5802 /*if(m_control.range_all == false &&
5803 d - 0.5*BS*MAP_BLOCKSIZE > range)
5808 Update expired mesh (used for day/night change)
5810 It doesn't work exactly like it should now with the
5811 tasked mesh update but whatever.
5814 bool mesh_expired = false;
5817 JMutexAutoLock lock(block->mesh_mutex);
5819 mesh_expired = block->getMeshExpired();
5821 // Mesh has not been expired and there is no mesh:
5822 // block has no content
5823 if(block->mesh == NULL && mesh_expired == false)
5827 f32 faraway = BS*50;
5828 //f32 faraway = m_control.wanted_range * BS;
5831 This has to be done with the mesh_mutex unlocked
5833 // Pretty random but this should work somewhat nicely
5834 if(mesh_expired && (
5835 (mesh_update_count < 3
5836 && (d < faraway || mesh_update_count < 2)
5839 (m_control.range_all && mesh_update_count < 20)
5842 /*if(mesh_expired && mesh_update_count < 6
5843 && (d < faraway || mesh_update_count < 3))*/
5845 mesh_update_count++;
5847 // Mesh has been expired: generate new mesh
5848 //block->updateMesh(daynight_ratio);
5849 m_client->addUpdateMeshTask(block->getPos());
5851 mesh_expired = false;
5856 Draw the faces of the block
5859 JMutexAutoLock lock(block->mesh_mutex);
5861 scene::SMesh *mesh = block->mesh;
5866 blocks_would_have_drawn++;
5867 if(blocks_drawn >= m_control.wanted_max_blocks
5868 && m_control.range_all == false
5869 && d > m_control.wanted_min_range * BS)
5873 u32 c = mesh->getMeshBufferCount();
5875 for(u32 i=0; i<c; i++)
5877 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
5878 const video::SMaterial& material = buf->getMaterial();
5879 video::IMaterialRenderer* rnd =
5880 driver->getMaterialRenderer(material.MaterialType);
5881 bool transparent = (rnd && rnd->isTransparent());
5882 // Render transparent on transparent pass and likewise.
5883 if(transparent == is_transparent_pass)
5886 This *shouldn't* hurt too much because Irrlicht
5887 doesn't change opengl textures if the old
5888 material is set again.
5890 driver->setMaterial(buf->getMaterial());
5891 driver->drawMeshBuffer(buf);
5892 vertex_count += buf->getVertexCount();
5896 } // foreach sectorblocks
5899 m_control.blocks_drawn = blocks_drawn;
5900 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
5902 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
5903 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
5906 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
5907 core::map<v3s16, MapBlock*> *affected_blocks)
5909 bool changed = false;
5911 Add it to all blocks touching it
5914 v3s16(0,0,0), // this
5915 v3s16(0,0,1), // back
5916 v3s16(0,1,0), // top
5917 v3s16(1,0,0), // right
5918 v3s16(0,0,-1), // front
5919 v3s16(0,-1,0), // bottom
5920 v3s16(-1,0,0), // left
5922 for(u16 i=0; i<7; i++)
5924 v3s16 p2 = p + dirs[i];
5925 // Block position of neighbor (or requested) node
5926 v3s16 blockpos = getNodeBlockPos(p2);
5927 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5928 if(blockref == NULL)
5930 // Relative position of requested node
5931 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5932 if(blockref->setTempMod(relpos, mod))
5937 if(changed && affected_blocks!=NULL)
5939 for(u16 i=0; i<7; i++)
5941 v3s16 p2 = p + dirs[i];
5942 // Block position of neighbor (or requested) node
5943 v3s16 blockpos = getNodeBlockPos(p2);
5944 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5945 if(blockref == NULL)
5947 affected_blocks->insert(blockpos, blockref);
5953 bool ClientMap::clearTempMod(v3s16 p,
5954 core::map<v3s16, MapBlock*> *affected_blocks)
5956 bool changed = false;
5958 v3s16(0,0,0), // this
5959 v3s16(0,0,1), // back
5960 v3s16(0,1,0), // top
5961 v3s16(1,0,0), // right
5962 v3s16(0,0,-1), // front
5963 v3s16(0,-1,0), // bottom
5964 v3s16(-1,0,0), // left
5966 for(u16 i=0; i<7; i++)
5968 v3s16 p2 = p + dirs[i];
5969 // Block position of neighbor (or requested) node
5970 v3s16 blockpos = getNodeBlockPos(p2);
5971 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5972 if(blockref == NULL)
5974 // Relative position of requested node
5975 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5976 if(blockref->clearTempMod(relpos))
5981 if(changed && affected_blocks!=NULL)
5983 for(u16 i=0; i<7; i++)
5985 v3s16 p2 = p + dirs[i];
5986 // Block position of neighbor (or requested) node
5987 v3s16 blockpos = getNodeBlockPos(p2);
5988 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5989 if(blockref == NULL)
5991 affected_blocks->insert(blockpos, blockref);
5997 void ClientMap::expireMeshes(bool only_daynight_diffed)
5999 TimeTaker timer("expireMeshes()");
6001 core::map<v2s16, MapSector*>::Iterator si;
6002 si = m_sectors.getIterator();
6003 for(; si.atEnd() == false; si++)
6005 MapSector *sector = si.getNode()->getValue();
6007 core::list< MapBlock * > sectorblocks;
6008 sector->getBlocks(sectorblocks);
6010 core::list< MapBlock * >::Iterator i;
6011 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
6013 MapBlock *block = *i;
6015 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
6021 JMutexAutoLock lock(block->mesh_mutex);
6022 if(block->mesh != NULL)
6024 /*block->mesh->drop();
6025 block->mesh = NULL;*/
6026 block->setMeshExpired(true);
6033 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
6035 assert(mapType() == MAPTYPE_CLIENT);
6038 v3s16 p = blockpos + v3s16(0,0,0);
6039 MapBlock *b = getBlockNoCreate(p);
6040 b->updateMesh(daynight_ratio);
6041 //b->setMeshExpired(true);
6043 catch(InvalidPositionException &e){}
6046 v3s16 p = blockpos + v3s16(-1,0,0);
6047 MapBlock *b = getBlockNoCreate(p);
6048 b->updateMesh(daynight_ratio);
6049 //b->setMeshExpired(true);
6051 catch(InvalidPositionException &e){}
6053 v3s16 p = blockpos + v3s16(0,-1,0);
6054 MapBlock *b = getBlockNoCreate(p);
6055 b->updateMesh(daynight_ratio);
6056 //b->setMeshExpired(true);
6058 catch(InvalidPositionException &e){}
6060 v3s16 p = blockpos + v3s16(0,0,-1);
6061 MapBlock *b = getBlockNoCreate(p);
6062 b->updateMesh(daynight_ratio);
6063 //b->setMeshExpired(true);
6065 catch(InvalidPositionException &e){}
6070 Update mesh of block in which the node is, and if the node is at the
6071 leading edge, update the appropriate leading blocks too.
6073 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
6081 v3s16 blockposes[4];
6082 for(u32 i=0; i<4; i++)
6084 v3s16 np = nodepos + dirs[i];
6085 blockposes[i] = getNodeBlockPos(np);
6086 // Don't update mesh of block if it has been done already
6087 bool already_updated = false;
6088 for(u32 j=0; j<i; j++)
6090 if(blockposes[j] == blockposes[i])
6092 already_updated = true;
6099 MapBlock *b = getBlockNoCreate(blockposes[i]);
6100 b->updateMesh(daynight_ratio);
6105 void ClientMap::PrintInfo(std::ostream &out)
6116 MapVoxelManipulator::MapVoxelManipulator(Map *map)
6121 MapVoxelManipulator::~MapVoxelManipulator()
6123 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
6127 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
6129 TimeTaker timer1("emerge", &emerge_time);
6131 // Units of these are MapBlocks
6132 v3s16 p_min = getNodeBlockPos(a.MinEdge);
6133 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
6135 VoxelArea block_area_nodes
6136 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6138 addArea(block_area_nodes);
6140 for(s32 z=p_min.Z; z<=p_max.Z; z++)
6141 for(s32 y=p_min.Y; y<=p_max.Y; y++)
6142 for(s32 x=p_min.X; x<=p_max.X; x++)
6145 core::map<v3s16, bool>::Node *n;
6146 n = m_loaded_blocks.find(p);
6150 bool block_data_inexistent = false;
6153 TimeTaker timer1("emerge load", &emerge_load_time);
6155 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
6156 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
6159 dstream<<std::endl;*/
6161 MapBlock *block = m_map->getBlockNoCreate(p);
6162 if(block->isDummy())
6163 block_data_inexistent = true;
6165 block->copyTo(*this);
6167 catch(InvalidPositionException &e)
6169 block_data_inexistent = true;
6172 if(block_data_inexistent)
6174 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6175 // Fill with VOXELFLAG_INEXISTENT
6176 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
6177 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
6179 s32 i = m_area.index(a.MinEdge.X,y,z);
6180 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
6184 m_loaded_blocks.insert(p, !block_data_inexistent);
6187 //dstream<<"emerge done"<<std::endl;
6191 SUGG: Add an option to only update eg. water and air nodes.
6192 This will make it interfere less with important stuff if
6195 void MapVoxelManipulator::blitBack
6196 (core::map<v3s16, MapBlock*> & modified_blocks)
6198 if(m_area.getExtent() == v3s16(0,0,0))
6201 //TimeTaker timer1("blitBack");
6203 /*dstream<<"blitBack(): m_loaded_blocks.size()="
6204 <<m_loaded_blocks.size()<<std::endl;*/
6207 Initialize block cache
6209 v3s16 blockpos_last;
6210 MapBlock *block = NULL;
6211 bool block_checked_in_modified = false;
6213 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
6214 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
6215 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
6219 u8 f = m_flags[m_area.index(p)];
6220 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
6223 MapNode &n = m_data[m_area.index(p)];
6225 v3s16 blockpos = getNodeBlockPos(p);
6230 if(block == NULL || blockpos != blockpos_last){
6231 block = m_map->getBlockNoCreate(blockpos);
6232 blockpos_last = blockpos;
6233 block_checked_in_modified = false;
6236 // Calculate relative position in block
6237 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
6239 // Don't continue if nothing has changed here
6240 if(block->getNode(relpos) == n)
6243 //m_map->setNode(m_area.MinEdge + p, n);
6244 block->setNode(relpos, n);
6247 Make sure block is in modified_blocks
6249 if(block_checked_in_modified == false)
6251 modified_blocks[blockpos] = block;
6252 block_checked_in_modified = true;
6255 catch(InvalidPositionException &e)
6261 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
6262 MapVoxelManipulator(map),
6263 m_create_area(false)
6267 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
6271 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
6273 // Just create the area so that it can be pointed to
6274 VoxelManipulator::emerge(a, caller_id);
6277 void ManualMapVoxelManipulator::initialEmerge(
6278 v3s16 blockpos_min, v3s16 blockpos_max)
6280 TimeTaker timer1("initialEmerge", &emerge_time);
6282 // Units of these are MapBlocks
6283 v3s16 p_min = blockpos_min;
6284 v3s16 p_max = blockpos_max;
6286 VoxelArea block_area_nodes
6287 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6289 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
6292 dstream<<"initialEmerge: area: ";
6293 block_area_nodes.print(dstream);
6294 dstream<<" ("<<size_MB<<"MB)";
6298 addArea(block_area_nodes);
6300 for(s32 z=p_min.Z; z<=p_max.Z; z++)
6301 for(s32 y=p_min.Y; y<=p_max.Y; y++)
6302 for(s32 x=p_min.X; x<=p_max.X; x++)
6305 core::map<v3s16, bool>::Node *n;
6306 n = m_loaded_blocks.find(p);
6310 bool block_data_inexistent = false;
6313 TimeTaker timer1("emerge load", &emerge_load_time);
6315 MapBlock *block = m_map->getBlockNoCreate(p);
6316 if(block->isDummy())
6317 block_data_inexistent = true;
6319 block->copyTo(*this);
6321 catch(InvalidPositionException &e)
6323 block_data_inexistent = true;
6326 if(block_data_inexistent)
6329 Mark area inexistent
6331 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6332 // Fill with VOXELFLAG_INEXISTENT
6333 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
6334 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
6336 s32 i = m_area.index(a.MinEdge.X,y,z);
6337 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
6341 m_loaded_blocks.insert(p, !block_data_inexistent);
6345 void ManualMapVoxelManipulator::blitBackAll(
6346 core::map<v3s16, MapBlock*> * modified_blocks)
6348 if(m_area.getExtent() == v3s16(0,0,0))
6352 Copy data of all blocks
6354 for(core::map<v3s16, bool>::Iterator
6355 i = m_loaded_blocks.getIterator();
6356 i.atEnd() == false; i++)
6358 bool existed = i.getNode()->getValue();
6359 if(existed == false)
6361 v3s16 p = i.getNode()->getKey();
6362 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
6365 dstream<<"WARNING: "<<__FUNCTION_NAME
6366 <<": got NULL block "
6367 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
6372 block->copyFrom(*this);
6375 modified_blocks->insert(p, block);