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 // Find ground level
3145 s16 surface_y = find_ground_level_clever(data->vmanip, p2d);
3147 if(surface_y > WATER_LEVEL + 2)
3151 v3s16 em = data->vmanip.m_area.getExtent();
3152 s16 y_start = surface_y;
3153 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3154 u32 not_sand_counter = 0;
3155 for(s16 y=y_start; y>=y_nodes_min; y--)
3157 MapNode *n = &data->vmanip.m_data[i];
3158 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
3160 n->d = CONTENT_SAND;
3165 if(not_sand_counter > 3)
3169 data->vmanip.m_area.add_y(em, i, -1);
3181 //TimeTaker timer1("generate trees");
3187 // Divide area into parts
3189 s16 sidelen = data->sectorpos_base_size*MAP_BLOCKSIZE / div;
3190 double area = sidelen * sidelen;
3191 for(s16 x0=0; x0<div; x0++)
3192 for(s16 z0=0; z0<div; z0++)
3194 // Center position of part of division
3196 data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen/2 + sidelen*x0,
3197 data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen/2 + sidelen*z0
3199 // Minimum edge of part of division
3201 data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen*x0,
3202 data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen*z0
3204 // Maximum edge of part of division
3206 data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen + sidelen*x0 - 1,
3207 data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen + sidelen*z0 - 1
3210 u32 tree_count = area * tree_amount_2d(data->seed, p2d_center);
3211 // Put trees in random places on part of division
3212 for(u32 i=0; i<tree_count; i++)
3214 s16 x = myrand_range(p2d_min.X, p2d_max.X);
3215 s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
3216 s16 y = find_ground_level(data->vmanip, v2s16(x,z));
3217 // Don't make a tree under water level
3220 // Don't make a tree so high that it doesn't fit
3221 if(y > y_nodes_max - 6)
3225 u32 i = data->vmanip.m_area.index(v3s16(p));
3226 MapNode *n = &data->vmanip.m_data[i];
3227 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS && n->d != CONTENT_SAND)
3229 // Trees grow only on mud and grass
3230 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
3233 make_tree(data->vmanip, p);
3235 // Cactii grow only on sand
3236 if(n->d == CONTENT_SAND)
3239 make_cactus(data->vmanip, p);
3244 /*u32 tree_max = relative_area / 60;
3245 //u32 count = myrand_range(0, tree_max);
3246 for(u32 i=0; i<count; i++)
3248 s16 x = myrand_range(0, data->sectorpos_base_size*MAP_BLOCKSIZE-1);
3249 s16 z = myrand_range(0, data->sectorpos_base_size*MAP_BLOCKSIZE-1);
3250 x += data->sectorpos_base.X*MAP_BLOCKSIZE;
3251 z += data->sectorpos_base.Y*MAP_BLOCKSIZE;
3252 s16 y = find_ground_level(data->vmanip, v2s16(x,z));
3253 // Don't make a tree under water level
3258 make_tree(data->vmanip, p);
3268 //TimeTaker timer1("grow grass");
3274 /*for(s16 x=0-4; x<data->sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
3275 for(s16 z=0-4; z<data->sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
3276 for(s16 x=0-data->max_spread_amount;
3277 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3279 for(s16 z=0-data->max_spread_amount;
3280 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3283 // Node position in 2d
3284 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3287 Find the lowest surface to which enough light ends up
3290 Basically just wait until not air and not leaves.
3294 v3s16 em = data->vmanip.m_area.getExtent();
3295 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
3297 // Go to ground level
3298 for(y=y_nodes_max; y>=y_nodes_min; y--)
3300 MapNode &n = data->vmanip.m_data[i];
3301 if(n.d != CONTENT_AIR
3302 && n.d != CONTENT_LEAVES)
3304 data->vmanip.m_area.add_y(em, i, -1);
3306 if(y >= y_nodes_min)
3309 surface_y = y_nodes_min;
3312 u32 i = data->vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
3313 MapNode *n = &data->vmanip.m_data[i];
3314 if(n->d == CONTENT_MUD)
3315 n->d = CONTENT_GRASS;
3322 Initial lighting (sunlight)
3325 core::map<v3s16, bool> light_sources;
3328 // 750ms @cs=8, can't optimize more
3329 TimeTaker timer1("initial lighting");
3331 // NOTE: This is no used... umm... for some reason!
3334 Go through the edges and add all nodes that have light to light_sources
3338 for(s16 i=0; i<4; i++)
3340 for(s16 j=lighting_min_d;
3347 if(i == 0 || i == 1)
3349 x = (i==0) ? lighting_min_d : lighting_max_d;
3358 z = (i==0) ? lighting_min_d : lighting_max_d;
3365 // Node position in 2d
3366 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3369 v3s16 em = data->vmanip.m_area.getExtent();
3370 s16 y_start = y_nodes_max;
3371 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3372 for(s16 y=y_start; y>=y_nodes_min; y--)
3374 MapNode *n = &data->vmanip.m_data[i];
3375 if(n->getLight(LIGHTBANK_DAY) != 0)
3377 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3379 //NOTE: This is broken, at least the index has to
3388 Go through the edges and apply sunlight to them, not caring
3393 for(s16 i=0; i<4; i++)
3395 for(s16 j=lighting_min_d;
3402 if(i == 0 || i == 1)
3404 x = (i==0) ? lighting_min_d : lighting_max_d;
3413 z = (i==0) ? lighting_min_d : lighting_max_d;
3420 // Node position in 2d
3421 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3423 // Loop from top to down
3425 u8 light = LIGHT_SUN;
3426 v3s16 em = data->vmanip.m_area.getExtent();
3427 s16 y_start = y_nodes_max;
3428 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3429 for(s16 y=y_start; y>=y_nodes_min; y--)
3431 MapNode *n = &data->vmanip.m_data[i];
3432 if(light_propagates_content(n->d) == false)
3436 else if(light != LIGHT_SUN
3437 || sunlight_propagates_content(n->d) == false)
3443 n->setLight(LIGHTBANK_DAY, light);
3444 n->setLight(LIGHTBANK_NIGHT, 0);
3448 // Insert light source
3449 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3452 // Increment index by y
3453 data->vmanip.m_area.add_y(em, i, -1);
3459 /*for(s16 x=0; x<data->sectorpos_base_size*MAP_BLOCKSIZE; x++)
3460 for(s16 z=0; z<data->sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3461 /*for(s16 x=0-data->max_spread_amount+1;
3462 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3464 for(s16 z=0-data->max_spread_amount+1;
3465 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3469 This has to be 1 smaller than the actual area, because
3470 neighboring nodes are checked.
3472 for(s16 x=lighting_min_d+1;
3473 x<=lighting_max_d-1;
3475 for(s16 z=lighting_min_d+1;
3476 z<=lighting_max_d-1;
3479 // Node position in 2d
3480 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3483 Apply initial sunlight
3486 u8 light = LIGHT_SUN;
3487 bool add_to_sources = false;
3488 v3s16 em = data->vmanip.m_area.getExtent();
3489 s16 y_start = y_nodes_max;
3490 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3491 for(s16 y=y_start; y>=y_nodes_min; y--)
3493 MapNode *n = &data->vmanip.m_data[i];
3495 if(light_propagates_content(n->d) == false)
3499 else if(light != LIGHT_SUN
3500 || sunlight_propagates_content(n->d) == false)
3506 // This doesn't take much time
3507 if(add_to_sources == false)
3510 Check sides. If side is not air or water, start
3511 adding to light_sources.
3514 v3s16(0,0,1), // back
3515 v3s16(1,0,0), // right
3516 v3s16(0,0,-1), // front
3517 v3s16(-1,0,0), // left
3519 for(u32 di=0; di<4; di++)
3521 v3s16 dirp = dirs4[di];
3523 data->vmanip.m_area.add_p(em, i2, dirp);
3524 MapNode *n2 = &data->vmanip.m_data[i2];
3526 n2->d != CONTENT_AIR
3527 && n2->d != CONTENT_WATERSOURCE
3528 && n2->d != CONTENT_WATER
3530 add_to_sources = true;
3536 n->setLight(LIGHTBANK_DAY, light);
3537 n->setLight(LIGHTBANK_NIGHT, 0);
3539 // This doesn't take much time
3540 if(light != 0 && add_to_sources)
3542 // Insert light source
3543 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3546 // Increment index by y
3547 data->vmanip.m_area.add_y(em, i, -1);
3555 // Spread light around
3557 TimeTaker timer("makeChunk() spreadLight");
3558 data->vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
3565 timer_generate.stop();
3568 //###################################################################
3569 //###################################################################
3570 //###################################################################
3571 //###################################################################
3572 //###################################################################
3573 //###################################################################
3574 //###################################################################
3575 //###################################################################
3576 //###################################################################
3577 //###################################################################
3578 //###################################################################
3579 //###################################################################
3580 //###################################################################
3581 //###################################################################
3582 //###################################################################
3584 void ServerMap::initChunkMake(ChunkMakeData &data, v2s16 chunkpos)
3586 if(m_chunksize == 0)
3594 // The distance how far into the neighbors the generator is allowed to go.
3595 s16 max_spread_amount_sectors = 2;
3596 assert(max_spread_amount_sectors <= m_chunksize);
3597 s16 max_spread_amount = max_spread_amount_sectors * MAP_BLOCKSIZE;
3599 s16 y_blocks_min = -4;
3600 s16 y_blocks_max = 3;
3602 v2s16 sectorpos_base = chunk_to_sector(chunkpos);
3603 s16 sectorpos_base_size = m_chunksize;
3605 v2s16 sectorpos_bigbase =
3606 sectorpos_base - v2s16(1,1) * max_spread_amount_sectors;
3607 s16 sectorpos_bigbase_size =
3608 sectorpos_base_size + 2 * max_spread_amount_sectors;
3611 const s16 limit = MAP_GENERATION_LIMIT / MAP_BLOCKSIZE;
3612 if(sectorpos_bigbase.X < -limit
3613 || sectorpos_bigbase.X + sectorpos_bigbase_size >= limit
3614 || sectorpos_bigbase.Y < -limit
3615 || sectorpos_bigbase.Y + sectorpos_bigbase_size >= limit)
3622 data.chunkpos = chunkpos;
3623 data.y_blocks_min = y_blocks_min;
3624 data.y_blocks_max = y_blocks_max;
3625 data.sectorpos_base = sectorpos_base;
3626 data.sectorpos_base_size = sectorpos_base_size;
3627 data.sectorpos_bigbase = sectorpos_bigbase;
3628 data.sectorpos_bigbase_size = sectorpos_bigbase_size;
3629 data.max_spread_amount = max_spread_amount;
3632 Create the whole area of this and the neighboring chunks
3635 TimeTaker timer("initChunkMake() create area");
3637 for(s16 x=0; x<sectorpos_bigbase_size; x++)
3638 for(s16 z=0; z<sectorpos_bigbase_size; z++)
3640 v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
3641 ServerMapSector *sector = createSector(sectorpos);
3644 for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
3646 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
3647 MapBlock *block = createBlock(blockpos);
3649 // Lighting won't be calculated
3650 //block->setLightingExpired(true);
3651 // Lighting will be calculated
3652 block->setLightingExpired(false);
3655 Block gets sunlight if this is true.
3657 This should be set to true when the top side of a block
3658 is completely exposed to the sky.
3660 Actually this doesn't matter now because the
3661 initial lighting is done here.
3663 block->setIsUnderground(y != y_blocks_max);
3669 Now we have a big empty area.
3671 Make a ManualMapVoxelManipulator that contains this and the
3675 v3s16 bigarea_blocks_min(
3676 sectorpos_bigbase.X,
3680 v3s16 bigarea_blocks_max(
3681 sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
3683 sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
3686 data.vmanip.setMap(this);
3689 TimeTaker timer("initChunkMake() initialEmerge");
3690 data.vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
3695 MapChunk* ServerMap::finishChunkMake(ChunkMakeData &data,
3696 core::map<v3s16, MapBlock*> &changed_blocks)
3702 Blit generated stuff to map
3706 //TimeTaker timer("generateChunkRaw() blitBackAll");
3707 data.vmanip.blitBackAll(&changed_blocks);
3711 Update day/night difference cache of the MapBlocks
3714 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
3715 i.atEnd() == false; i++)
3717 MapBlock *block = i.getNode()->getValue();
3718 block->updateDayNightDiff();
3723 Copy transforming liquid information
3725 while(data.transforming_liquid.size() > 0)
3727 v3s16 p = data.transforming_liquid.pop_front();
3728 m_transforming_liquid.push_back(p);
3732 Add random objects to blocks
3735 for(s16 x=0; x<data.sectorpos_base_size; x++)
3736 for(s16 z=0; z<data.sectorpos_base_size; z++)
3738 v2s16 sectorpos = data.sectorpos_base + v2s16(x,z);
3739 ServerMapSector *sector = createSector(sectorpos);
3742 for(s16 y=data.y_blocks_min; y<=data.y_blocks_max; y++)
3744 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
3745 MapBlock *block = createBlock(blockpos);
3746 addRandomObjects(block);
3752 Create chunk metadata
3755 for(s16 x=-1; x<=1; x++)
3756 for(s16 y=-1; y<=1; y++)
3758 v2s16 chunkpos0 = data.chunkpos + v2s16(x,y);
3759 // Add chunk meta information
3760 MapChunk *chunk = getChunk(chunkpos0);
3763 chunk = new MapChunk();
3764 m_chunks.insert(chunkpos0, chunk);
3766 //chunk->setIsVolatile(true);
3767 if(chunk->getGenLevel() > GENERATED_PARTLY)
3768 chunk->setGenLevel(GENERATED_PARTLY);
3772 Set central chunk non-volatile
3774 MapChunk *chunk = getChunk(data.chunkpos);
3777 //chunk->setIsVolatile(false);
3778 chunk->setGenLevel(GENERATED_FULLY);
3781 Save changed parts of map
3790 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
3791 core::map<v3s16, MapBlock*> &changed_blocks,
3794 DSTACK(__FUNCTION_NAME);
3797 Don't generate if already fully generated
3801 MapChunk *chunk = getChunk(chunkpos);
3802 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3804 dstream<<"generateChunkRaw(): Chunk "
3805 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
3806 <<" already generated"<<std::endl;
3811 dstream<<"generateChunkRaw(): Generating chunk "
3812 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
3815 TimeTaker timer("generateChunkRaw()");
3819 // Initialize generation
3820 initChunkMake(data, chunkpos);
3825 // Finalize generation
3826 MapChunk *chunk = finishChunkMake(data, changed_blocks);
3829 Return central chunk (which was requested)
3835 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
3836 core::map<v3s16, MapBlock*> &changed_blocks)
3838 dstream<<"generateChunk(): Generating chunk "
3839 <<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
3842 /*for(s16 x=-1; x<=1; x++)
3843 for(s16 y=-1; y<=1; y++)*/
3844 for(s16 x=-0; x<=0; x++)
3845 for(s16 y=-0; y<=0; y++)
3847 v2s16 chunkpos0 = chunkpos1 + v2s16(x,y);
3848 MapChunk *chunk = getChunk(chunkpos0);
3849 // Skip if already generated
3850 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3852 generateChunkRaw(chunkpos0, changed_blocks);
3855 assert(chunkNonVolatile(chunkpos1));
3857 MapChunk *chunk = getChunk(chunkpos1);
3862 ServerMapSector * ServerMap::createSector(v2s16 p2d)
3864 DSTACKF("%s: p2d=(%d,%d)",
3869 Check if it exists already in memory
3871 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3876 Try to load it from disk (with blocks)
3878 if(loadSectorFull(p2d) == true)
3880 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3883 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
3884 throw InvalidPositionException("");
3890 Do not create over-limit
3892 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3893 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3894 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3895 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3896 throw InvalidPositionException("createSector(): pos. over limit");
3899 Generate blank sector
3902 sector = new ServerMapSector(this, p2d);
3904 // Sector position on map in nodes
3905 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3910 m_sectors.insert(p2d, sector);
3916 MapSector * ServerMap::emergeSector(v2s16 p2d,
3917 core::map<v3s16, MapBlock*> &changed_blocks)
3919 DSTACK("%s: p2d=(%d,%d)",
3926 v2s16 chunkpos = sector_to_chunk(p2d);
3927 /*bool chunk_nonvolatile = false;
3928 MapChunk *chunk = getChunk(chunkpos);
3929 if(chunk && chunk->getIsVolatile() == false)
3930 chunk_nonvolatile = true;*/
3931 bool chunk_nonvolatile = chunkNonVolatile(chunkpos);
3934 If chunk is not fully generated, generate chunk
3936 if(chunk_nonvolatile == false)
3938 // Generate chunk and neighbors
3939 generateChunk(chunkpos, changed_blocks);
3943 Return sector if it exists now
3945 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3950 Try to load it from disk
3952 if(loadSectorFull(p2d) == true)
3954 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3957 dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
3958 throw InvalidPositionException("");
3964 generateChunk should have generated the sector
3968 dstream<<"WARNING: ServerMap::emergeSector: Cannot find sector ("
3969 <<p2d.X<<","<<p2d.Y<<" and chunk is already generated. "
3973 dstream<<"WARNING: Creating an empty sector."<<std::endl;
3975 return createSector(p2d);
3980 dstream<<"WARNING: Forcing regeneration of chunk."<<std::endl;
3983 generateChunkRaw(chunkpos, changed_blocks, true);
3986 Return sector if it exists now
3988 sector = getSectorNoGenerateNoEx(p2d);
3992 dstream<<"ERROR: Could not get sector from anywhere."<<std::endl;
4000 //return generateSector();
4005 NOTE: This is not used for main map generation, only for blocks
4006 that are very high or low
4008 MapBlock * ServerMap::generateBlock(
4010 MapBlock *original_dummy,
4011 ServerMapSector *sector,
4012 core::map<v3s16, MapBlock*> &changed_blocks,
4013 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4016 DSTACKF("%s: p=(%d,%d,%d)",
4020 // If chunks are disabled
4021 /*if(m_chunksize == 0)
4023 dstream<<"ServerMap::generateBlock(): Chunks disabled -> "
4024 <<"not generating."<<std::endl;
4028 /*dstream<<"generateBlock(): "
4029 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4032 MapBlock *block = original_dummy;
4034 v2s16 p2d(p.X, p.Z);
4036 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
4039 Do not generate over-limit
4041 if(blockpos_over_limit(p))
4043 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
4044 throw InvalidPositionException("generateBlock(): pos. over limit");
4048 If block doesn't exist, create one.
4049 If it exists, it is a dummy. In that case unDummify() it.
4051 NOTE: This already sets the map as the parent of the block
4055 block = sector->createBlankBlockNoInsert(block_y);
4059 // Remove the block so that nobody can get a half-generated one.
4060 sector->removeBlock(block);
4061 // Allocate the block to contain the generated data
4067 Generate a completely empty block
4069 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4070 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4072 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4076 block->setNode(v3s16(x0,y0,z0), n);
4081 Generate a proper block
4084 u8 water_material = CONTENT_WATERSOURCE;
4086 s32 lowest_ground_y = 32767;
4087 s32 highest_ground_y = -32768;
4089 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4090 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4092 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
4094 //s16 surface_y = 0;
4096 s16 mud_add_amount = get_mud_add_amount(m_seed, p2d_nodes+v2s16(x0,z0));
4098 s16 surface_y = base_rock_level_2d(m_seed, p2d_nodes+v2s16(x0,z0))
4100 // If chunks are disabled
4101 if(m_chunksize == 0)
4102 surface_y = WATER_LEVEL + 1;
4104 if(surface_y < lowest_ground_y)
4105 lowest_ground_y = surface_y;
4106 if(surface_y > highest_ground_y)
4107 highest_ground_y = surface_y;
4109 s32 surface_depth = AVERAGE_MUD_AMOUNT;
4111 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4113 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
4118 NOTE: If there are some man-made structures above the
4119 newly created block, they won't be taken into account.
4121 if(real_y > surface_y)
4122 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
4128 // If node is over heightmap y, it's air or water
4129 if(real_y > surface_y)
4131 // If under water level, it's water
4132 if(real_y < WATER_LEVEL)
4134 n.d = water_material;
4135 n.setLight(LIGHTBANK_DAY,
4136 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
4138 Add to transforming liquid queue (in case it'd
4141 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
4142 m_transforming_liquid.push_back(real_pos);
4148 // Else it's ground or caves (air)
4151 // If it's surface_depth under ground, it's stone
4152 if(real_y <= surface_y - surface_depth)
4154 n.d = CONTENT_STONE;
4158 // It is mud if it is under the first ground
4159 // level or under water
4160 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
4166 n.d = CONTENT_GRASS;
4169 //n.d = CONTENT_MUD;
4171 /*// If under water level, it's mud
4172 if(real_y < WATER_LEVEL)
4174 // Only the topmost node is grass
4175 else if(real_y <= surface_y - 1)
4178 n.d = CONTENT_GRASS;*/
4182 block->setNode(v3s16(x0,y0,z0), n);
4187 Calculate some helper variables
4190 // Completely underground if the highest part of block is under lowest
4192 // This has to be very sure; it's probably one too strict now but
4193 // that's just better.
4194 bool completely_underground =
4195 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
4197 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
4199 bool mostly_underwater_surface = false;
4200 if(highest_ground_y < WATER_LEVEL
4201 && some_part_underground && !completely_underground)
4202 mostly_underwater_surface = true;
4205 Get local attributes
4208 //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
4210 float caves_amount = 0.5;
4215 NOTE: BEWARE: Too big amount of attribute points slows verything
4217 1 interpolation from 5000 points takes 2-3ms.
4219 //TimeTaker timer("generateBlock() local attribute retrieval");
4220 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
4221 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
4222 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
4226 //dstream<<"generateBlock(): Done"<<std::endl;
4232 // Initialize temporary table
4233 const s32 ued = MAP_BLOCKSIZE;
4234 bool underground_emptiness[ued*ued*ued];
4235 for(s32 i=0; i<ued*ued*ued; i++)
4237 underground_emptiness[i] = 0;
4244 Initialize orp and ors. Try to find if some neighboring
4245 MapBlock has a tunnel ended in its side
4249 (float)(myrand()%ued)+0.5,
4250 (float)(myrand()%ued)+0.5,
4251 (float)(myrand()%ued)+0.5
4254 bool found_existing = false;
4260 for(s16 y=0; y<ued; y++)
4261 for(s16 x=0; x<ued; x++)
4263 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4264 if(getNode(ap).d == CONTENT_AIR)
4266 orp = v3f(x+1,y+1,0);
4267 found_existing = true;
4268 goto continue_generating;
4272 catch(InvalidPositionException &e){}
4278 for(s16 y=0; y<ued; y++)
4279 for(s16 x=0; x<ued; x++)
4281 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4282 if(getNode(ap).d == CONTENT_AIR)
4284 orp = v3f(x+1,y+1,ued-1);
4285 found_existing = true;
4286 goto continue_generating;
4290 catch(InvalidPositionException &e){}
4296 for(s16 y=0; y<ued; y++)
4297 for(s16 z=0; z<ued; z++)
4299 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4300 if(getNode(ap).d == CONTENT_AIR)
4302 orp = v3f(0,y+1,z+1);
4303 found_existing = true;
4304 goto continue_generating;
4308 catch(InvalidPositionException &e){}
4314 for(s16 y=0; y<ued; y++)
4315 for(s16 z=0; z<ued; z++)
4317 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4318 if(getNode(ap).d == CONTENT_AIR)
4320 orp = v3f(ued-1,y+1,z+1);
4321 found_existing = true;
4322 goto continue_generating;
4326 catch(InvalidPositionException &e){}
4332 for(s16 x=0; x<ued; x++)
4333 for(s16 z=0; z<ued; z++)
4335 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4336 if(getNode(ap).d == CONTENT_AIR)
4338 orp = v3f(x+1,0,z+1);
4339 found_existing = true;
4340 goto continue_generating;
4344 catch(InvalidPositionException &e){}
4350 for(s16 x=0; x<ued; x++)
4351 for(s16 z=0; z<ued; z++)
4353 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4354 if(getNode(ap).d == CONTENT_AIR)
4356 orp = v3f(x+1,ued-1,z+1);
4357 found_existing = true;
4358 goto continue_generating;
4362 catch(InvalidPositionException &e){}
4364 continue_generating:
4367 Choose whether to actually generate cave
4369 bool do_generate_caves = true;
4370 // Don't generate if no part is underground
4371 if(!some_part_underground)
4373 do_generate_caves = false;
4375 // Don't generate if mostly underwater surface
4376 /*else if(mostly_underwater_surface)
4378 do_generate_caves = false;
4380 // Partly underground = cave
4381 else if(!completely_underground)
4383 do_generate_caves = (rand() % 100 <= (s32)(caves_amount*100));
4385 // Found existing cave underground
4386 else if(found_existing && completely_underground)
4388 do_generate_caves = (rand() % 100 <= (s32)(caves_amount*100));
4390 // Underground and no caves found
4393 do_generate_caves = (rand() % 300 <= (s32)(caves_amount*100));
4396 if(do_generate_caves)
4399 Generate some tunnel starting from orp and ors
4401 for(u16 i=0; i<3; i++)
4404 (float)(myrand()%ued)+0.5,
4405 (float)(myrand()%ued)+0.5,
4406 (float)(myrand()%ued)+0.5
4410 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
4414 for(float f=0; f<1.0; f+=0.04)
4416 v3f fp = orp + vec * f;
4417 v3s16 cp(fp.X, fp.Y, fp.Z);
4419 s16 d1 = d0 + rs - 1;
4420 for(s16 z0=d0; z0<=d1; z0++)
4422 s16 si = rs - abs(z0);
4423 for(s16 x0=-si; x0<=si-1; x0++)
4425 s16 si2 = rs - abs(x0);
4426 for(s16 y0=-si2+1; y0<=si2-1; y0++)
4432 if(isInArea(p, ued) == false)
4434 underground_emptiness[ued*ued*z + ued*y + x] = 1;
4446 // Set to true if has caves.
4447 // Set when some non-air is changed to air when making caves.
4448 bool has_caves = false;
4451 Apply temporary cave data to block
4454 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4455 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4457 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4459 MapNode n = block->getNode(v3s16(x0,y0,z0));
4462 if(underground_emptiness[
4463 ued*ued*(z0*ued/MAP_BLOCKSIZE)
4464 +ued*(y0*ued/MAP_BLOCKSIZE)
4465 +(x0*ued/MAP_BLOCKSIZE)])
4467 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
4476 block->setNode(v3s16(x0,y0,z0), n);
4481 This is used for guessing whether or not the block should
4482 receive sunlight from the top if the block above doesn't exist
4484 block->setIsUnderground(completely_underground);
4487 Force lighting update if some part of block is partly
4488 underground and has caves.
4490 /*if(some_part_underground && !completely_underground && has_caves)
4492 //dstream<<"Half-ground caves"<<std::endl;
4493 lighting_invalidated_blocks[block->getPos()] = block;
4496 // DEBUG: Always update lighting
4497 //lighting_invalidated_blocks[block->getPos()] = block;
4503 if(some_part_underground)
4505 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
4510 for(s16 i=0; i<underground_level/4 + 1; i++)
4512 if(myrand()%50 == 0)
4515 (myrand()%(MAP_BLOCKSIZE-2))+1,
4516 (myrand()%(MAP_BLOCKSIZE-2))+1,
4517 (myrand()%(MAP_BLOCKSIZE-2))+1
4523 for(u16 i=0; i<27; i++)
4525 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4527 block->setNode(cp+g_27dirs[i], n);
4535 u16 coal_amount = 30;
4536 u16 coal_rareness = 60 / coal_amount;
4537 if(coal_rareness == 0)
4539 if(myrand()%coal_rareness == 0)
4541 u16 a = myrand() % 16;
4542 u16 amount = coal_amount * a*a*a / 1000;
4543 for(s16 i=0; i<amount; i++)
4546 (myrand()%(MAP_BLOCKSIZE-2))+1,
4547 (myrand()%(MAP_BLOCKSIZE-2))+1,
4548 (myrand()%(MAP_BLOCKSIZE-2))+1
4552 n.d = CONTENT_STONE;
4553 n.param = MINERAL_COAL;
4555 for(u16 i=0; i<27; i++)
4557 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4559 block->setNode(cp+g_27dirs[i], n);
4567 //TODO: change to iron_amount or whatever
4568 u16 iron_amount = 15;
4569 u16 iron_rareness = 60 / iron_amount;
4570 if(iron_rareness == 0)
4572 if(myrand()%iron_rareness == 0)
4574 u16 a = myrand() % 16;
4575 u16 amount = iron_amount * a*a*a / 1000;
4576 for(s16 i=0; i<amount; i++)
4579 (myrand()%(MAP_BLOCKSIZE-2))+1,
4580 (myrand()%(MAP_BLOCKSIZE-2))+1,
4581 (myrand()%(MAP_BLOCKSIZE-2))+1
4585 n.d = CONTENT_STONE;
4586 n.param = MINERAL_IRON;
4588 for(u16 i=0; i<27; i++)
4590 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4592 block->setNode(cp+g_27dirs[i], n);
4599 Create a few rats in empty blocks underground
4601 if(completely_underground)
4603 //for(u16 i=0; i<2; i++)
4606 (myrand()%(MAP_BLOCKSIZE-2))+1,
4607 (myrand()%(MAP_BLOCKSIZE-2))+1,
4608 (myrand()%(MAP_BLOCKSIZE-2))+1
4611 // Check that the place is empty
4612 //if(!is_ground_content(block->getNode(cp).d))
4615 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp, BS));
4616 block->addObject(obj);
4621 #endif // end of proper block generation
4624 Add block to sector.
4626 sector->insertBlock(block);
4628 // Lighting is invalid after generation.
4629 block->setLightingExpired(true);
4636 <<"lighting_invalidated_blocks.size()"
4640 <<" "<<lighting_invalidated_blocks.size()
4642 <<", "<<completely_underground
4643 <<", "<<some_part_underground
4650 MapBlock * ServerMap::createBlock(v3s16 p)
4652 DSTACKF("%s: p=(%d,%d,%d)",
4653 __FUNCTION_NAME, p.X, p.Y, p.Z);
4656 Do not create over-limit
4658 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4659 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4660 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4661 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4662 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4663 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4664 throw InvalidPositionException("createBlock(): pos. over limit");
4666 v2s16 p2d(p.X, p.Z);
4669 This will create or load a sector if not found in memory.
4670 If block exists on disk, it will be loaded.
4672 NOTE: On old save formats, this will be slow, as it generates
4673 lighting on blocks for them.
4675 ServerMapSector *sector;
4677 sector = (ServerMapSector*)createSector(p2d);
4678 assert(sector->getId() == MAPSECTOR_SERVER);
4680 catch(InvalidPositionException &e)
4682 dstream<<"createBlock: createSector() failed"<<std::endl;
4686 NOTE: This should not be done, or at least the exception
4687 should not be passed on as std::exception, because it
4688 won't be catched at all.
4690 /*catch(std::exception &e)
4692 dstream<<"createBlock: createSector() failed: "
4693 <<e.what()<<std::endl;
4698 Try to get a block from the sector
4701 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4705 block = sector->createBlankBlock(block_y);
4709 MapBlock * ServerMap::emergeBlock(
4711 bool only_from_disk,
4712 core::map<v3s16, MapBlock*> &changed_blocks,
4713 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4716 DSTACKF("%s: p=(%d,%d,%d), only_from_disk=%d",
4718 p.X, p.Y, p.Z, only_from_disk);
4721 Do not generate over-limit
4723 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4724 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4725 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4726 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4727 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4728 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4729 throw InvalidPositionException("emergeBlock(): pos. over limit");
4731 v2s16 p2d(p.X, p.Z);
4734 This will create or load a sector if not found in memory.
4735 If block exists on disk, it will be loaded.
4737 ServerMapSector *sector;
4739 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
4740 assert(sector->getId() == MAPSECTOR_SERVER);
4742 catch(InvalidPositionException &e)
4744 dstream<<"emergeBlock: emergeSector() failed: "
4745 <<e.what()<<std::endl;
4746 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4748 <<"You could try to delete it."<<std::endl;
4751 catch(VersionMismatchException &e)
4753 dstream<<"emergeBlock: emergeSector() failed: "
4754 <<e.what()<<std::endl;
4755 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4757 <<"You could try to delete it."<<std::endl;
4761 NOTE: This should not be done, or at least the exception
4762 should not be passed on as std::exception, because it
4763 won't be catched at all.
4765 /*catch(std::exception &e)
4767 dstream<<"emergeBlock: emergeSector() failed: "
4768 <<e.what()<<std::endl;
4769 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4771 <<"You could try to delete it."<<std::endl;
4776 Try to get a block from the sector
4779 bool does_not_exist = false;
4780 bool lighting_expired = false;
4781 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4785 does_not_exist = true;
4787 else if(block->isDummy() == true)
4789 does_not_exist = true;
4791 else if(block->getLightingExpired())
4793 lighting_expired = true;
4798 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
4803 If block was not found on disk and not going to generate a
4804 new one, make sure there is a dummy block in place.
4806 if(only_from_disk && (does_not_exist || lighting_expired))
4808 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
4812 // Create dummy block
4813 block = new MapBlock(this, p, true);
4815 // Add block to sector
4816 sector->insertBlock(block);
4822 //dstream<<"Not found on disk, generating."<<std::endl;
4824 //TimeTaker("emergeBlock() generate");
4826 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
4829 If the block doesn't exist, generate the block.
4833 block = generateBlock(p, block, sector, changed_blocks,
4834 lighting_invalidated_blocks);
4837 if(lighting_expired)
4839 lighting_invalidated_blocks.insert(p, block);
4843 Initially update sunlight
4847 core::map<v3s16, bool> light_sources;
4848 bool black_air_left = false;
4849 bool bottom_invalid =
4850 block->propagateSunlight(light_sources, true,
4851 &black_air_left, true);
4853 // If sunlight didn't reach everywhere and part of block is
4854 // above ground, lighting has to be properly updated
4855 //if(black_air_left && some_part_underground)
4858 lighting_invalidated_blocks[block->getPos()] = block;
4863 lighting_invalidated_blocks[block->getPos()] = block;
4870 s16 ServerMap::findGroundLevel(v2s16 p2d)
4873 Uh, just do something random...
4875 // Find existing map from top to down
4878 v3s16 p(p2d.X, max, p2d.Y);
4879 for(; p.Y>min; p.Y--)
4881 MapNode n = getNodeNoEx(p);
4882 if(n.d != CONTENT_IGNORE)
4887 // If this node is not air, go to plan b
4888 if(getNodeNoEx(p).d != CONTENT_AIR)
4890 // Search existing walkable and return it
4891 for(; p.Y>min; p.Y--)
4893 MapNode n = getNodeNoEx(p);
4894 if(content_walkable(n.d) && n.d != CONTENT_IGNORE)
4900 Plan B: Get from map generator perlin noise function
4902 // This won't work if proper generation is disabled
4903 if(m_chunksize == 0)
4904 return WATER_LEVEL+2;
4905 double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
4909 void ServerMap::createDirs(std::string path)
4911 if(fs::CreateAllDirs(path) == false)
4913 m_dout<<DTIME<<"ServerMap: Failed to create directory "
4914 <<"\""<<path<<"\""<<std::endl;
4915 throw BaseException("ServerMap failed to create directory");
4919 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
4925 snprintf(cc, 9, "%.4x%.4x",
4926 (unsigned int)pos.X&0xffff,
4927 (unsigned int)pos.Y&0xffff);
4929 return m_savedir + "/sectors/" + cc;
4931 snprintf(cc, 9, "%.3x/%.3x",
4932 (unsigned int)pos.X&0xfff,
4933 (unsigned int)pos.Y&0xfff);
4935 return m_savedir + "/sectors2/" + cc;
4941 v2s16 ServerMap::getSectorPos(std::string dirname)
4945 size_t spos = dirname.rfind('/') + 1;
4946 assert(spos != std::string::npos);
4947 if(dirname.size() - spos == 8)
4950 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
4952 else if(dirname.size() - spos == 3)
4955 r = sscanf(dirname.substr(spos-4).c_str(), "%3x/%3x", &x, &y);
4956 // Sign-extend the 12 bit values up to 16 bits...
4957 if(x&0x800) x|=0xF000;
4958 if(y&0x800) y|=0xF000;
4965 v2s16 pos((s16)x, (s16)y);
4969 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
4971 v2s16 p2d = getSectorPos(sectordir);
4973 if(blockfile.size() != 4){
4974 throw InvalidFilenameException("Invalid block filename");
4977 int r = sscanf(blockfile.c_str(), "%4x", &y);
4979 throw InvalidFilenameException("Invalid block filename");
4980 return v3s16(p2d.X, y, p2d.Y);
4983 void ServerMap::save(bool only_changed)
4985 DSTACK(__FUNCTION_NAME);
4986 if(m_map_saving_enabled == false)
4988 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
4992 if(only_changed == false)
4993 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
4996 if(only_changed == false || m_map_metadata_changed)
5001 // Disable saving chunk metadata if chunks are disabled
5002 if(m_chunksize != 0)
5004 if(only_changed == false || anyChunkModified())
5008 u32 sector_meta_count = 0;
5009 u32 block_count = 0;
5012 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5014 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
5015 for(; i.atEnd() == false; i++)
5017 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
5018 assert(sector->getId() == MAPSECTOR_SERVER);
5020 if(sector->differs_from_disk || only_changed == false)
5022 saveSectorMeta(sector);
5023 sector_meta_count++;
5025 core::list<MapBlock*> blocks;
5026 sector->getBlocks(blocks);
5027 core::list<MapBlock*>::Iterator j;
5028 for(j=blocks.begin(); j!=blocks.end(); j++)
5030 MapBlock *block = *j;
5031 if(block->getChangedFlag() || only_changed == false)
5036 /*dstream<<"ServerMap: Written block ("
5037 <<block->getPos().X<<","
5038 <<block->getPos().Y<<","
5039 <<block->getPos().Z<<")"
5048 Only print if something happened or saved whole map
5050 if(only_changed == false || sector_meta_count != 0
5051 || block_count != 0)
5053 dstream<<DTIME<<"ServerMap: Written: "
5054 <<sector_meta_count<<" sector metadata files, "
5055 <<block_count<<" block files"
5061 // NOTE: Doing this is insane. Deprecated and probably broken.
5062 void ServerMap::loadAll()
5064 DSTACK(__FUNCTION_NAME);
5065 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
5070 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
5072 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
5074 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5077 s32 printed_counter = -100000;
5078 s32 count = list.size();
5080 std::vector<fs::DirListNode>::iterator i;
5081 for(i=list.begin(); i!=list.end(); i++)
5083 if(counter > printed_counter + 10)
5085 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
5086 printed_counter = counter;
5090 MapSector *sector = NULL;
5092 // We want directories
5096 sector = loadSectorMeta(i->name);
5098 catch(InvalidFilenameException &e)
5100 // This catches unknown crap in directory
5103 std::vector<fs::DirListNode> list2 = fs::GetDirListing
5104 (m_savedir+"/sectors/"+i->name);
5105 std::vector<fs::DirListNode>::iterator i2;
5106 for(i2=list2.begin(); i2!=list2.end(); i2++)
5112 loadBlock(i->name, i2->name, sector);
5114 catch(InvalidFilenameException &e)
5116 // This catches unknown crap in directory
5120 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
5125 void ServerMap::saveMasterHeightmap()
5127 DSTACK(__FUNCTION_NAME);
5129 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
5131 createDir(m_savedir);
5133 /*std::string fullpath = m_savedir + "/master_heightmap";
5134 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5135 if(o.good() == false)
5136 throw FileNotGoodException("Cannot open master heightmap");*/
5138 // Format used for writing
5139 //u8 version = SER_FMT_VER_HIGHEST;
5142 void ServerMap::loadMasterHeightmap()
5144 DSTACK(__FUNCTION_NAME);
5146 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
5148 /*std::string fullpath = m_savedir + "/master_heightmap";
5149 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5150 if(is.good() == false)
5151 throw FileNotGoodException("Cannot open master heightmap");*/
5155 void ServerMap::saveMapMeta()
5157 DSTACK(__FUNCTION_NAME);
5159 dstream<<"INFO: ServerMap::saveMapMeta(): "
5160 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
5163 createDirs(m_savedir);
5165 std::string fullpath = m_savedir + "/map_meta.txt";
5166 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
5167 if(os.good() == false)
5169 dstream<<"ERROR: ServerMap::saveMapMeta(): "
5170 <<"could not open"<<fullpath<<std::endl;
5171 throw FileNotGoodException("Cannot open chunk metadata");
5175 params.setU64("seed", m_seed);
5176 params.setS32("chunksize", m_chunksize);
5178 params.writeLines(os);
5180 os<<"[end_of_params]\n";
5182 m_map_metadata_changed = false;
5185 void ServerMap::loadMapMeta()
5187 DSTACK(__FUNCTION_NAME);
5189 dstream<<"INFO: ServerMap::loadMapMeta(): Loading map metadata"
5192 std::string fullpath = m_savedir + "/map_meta.txt";
5193 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5194 if(is.good() == false)
5196 dstream<<"ERROR: ServerMap::loadMapMeta(): "
5197 <<"could not open"<<fullpath<<std::endl;
5198 throw FileNotGoodException("Cannot open map metadata");
5206 throw SerializationError
5207 ("ServerMap::loadMapMeta(): [end_of_params] not found");
5209 std::getline(is, line);
5210 std::string trimmedline = trim(line);
5211 if(trimmedline == "[end_of_params]")
5213 params.parseConfigLine(line);
5216 m_seed = params.getU64("seed");
5217 m_chunksize = params.getS32("chunksize");
5219 dstream<<"INFO: ServerMap::loadMapMeta(): "
5220 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
5224 void ServerMap::saveChunkMeta()
5226 DSTACK(__FUNCTION_NAME);
5228 // This should not be called if chunks are disabled.
5229 assert(m_chunksize != 0);
5231 u32 count = m_chunks.size();
5233 dstream<<"INFO: ServerMap::saveChunkMeta(): Saving metadata of "
5234 <<count<<" chunks"<<std::endl;
5236 createDirs(m_savedir);
5238 std::string fullpath = m_savedir + "/chunk_meta";
5239 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
5240 if(os.good() == false)
5242 dstream<<"ERROR: ServerMap::saveChunkMeta(): "
5243 <<"could not open"<<fullpath<<std::endl;
5244 throw FileNotGoodException("Cannot open chunk metadata");
5250 os.write((char*)&version, 1);
5255 writeU32(buf, count);
5256 os.write((char*)buf, 4);
5258 for(core::map<v2s16, MapChunk*>::Iterator
5259 i = m_chunks.getIterator();
5260 i.atEnd()==false; i++)
5262 v2s16 p = i.getNode()->getKey();
5263 MapChunk *chunk = i.getNode()->getValue();
5266 os.write((char*)buf, 4);
5268 chunk->serialize(os, version);
5271 setChunksNonModified();
5274 void ServerMap::loadChunkMeta()
5276 DSTACK(__FUNCTION_NAME);
5278 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading chunk metadata"
5281 std::string fullpath = m_savedir + "/chunk_meta";
5282 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5283 if(is.good() == false)
5285 dstream<<"ERROR: ServerMap::loadChunkMeta(): "
5286 <<"could not open"<<fullpath<<std::endl;
5287 throw FileNotGoodException("Cannot open chunk metadata");
5293 is.read((char*)&version, 1);
5298 is.read((char*)buf, 4);
5299 u32 count = readU32(buf);
5301 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading metadata of "
5302 <<count<<" chunks"<<std::endl;
5304 for(u32 i=0; i<count; i++)
5307 MapChunk *chunk = new MapChunk();
5309 is.read((char*)buf, 4);
5312 chunk->deSerialize(is, version);
5313 m_chunks.insert(p, chunk);
5317 void ServerMap::saveSectorMeta(ServerMapSector *sector)
5319 DSTACK(__FUNCTION_NAME);
5320 // Format used for writing
5321 u8 version = SER_FMT_VER_HIGHEST;
5323 v2s16 pos = sector->getPos();
5324 std::string dir = getSectorDir(pos);
5327 std::string fullpath = dir + "/meta";
5328 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5329 if(o.good() == false)
5330 throw FileNotGoodException("Cannot open sector metafile");
5332 sector->serialize(o, version);
5334 sector->differs_from_disk = false;
5337 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
5339 DSTACK(__FUNCTION_NAME);
5341 v2s16 p2d = getSectorPos(sectordir);
5343 ServerMapSector *sector = NULL;
5345 std::string fullpath = sectordir + "/meta";
5346 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5347 if(is.good() == false)
5349 // If the directory exists anyway, it probably is in some old
5350 // format. Just go ahead and create the sector.
5351 if(fs::PathExists(sectordir))
5353 dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
5354 <<fullpath<<" doesn't exist but directory does."
5355 <<" Continuing with a sector with no metadata."
5357 sector = new ServerMapSector(this, p2d);
5358 m_sectors.insert(p2d, sector);
5362 throw FileNotGoodException("Cannot open sector metafile");
5367 sector = ServerMapSector::deSerialize
5368 (is, this, p2d, m_sectors);
5370 saveSectorMeta(sector);
5373 sector->differs_from_disk = false;
5378 bool ServerMap::loadSectorFull(v2s16 p2d)
5380 DSTACK(__FUNCTION_NAME);
5382 MapSector *sector = NULL;
5384 // The directory layout we're going to load from.
5385 // 1 - original sectors/xxxxzzzz/
5386 // 2 - new sectors2/xxx/zzz/
5387 // If we load from anything but the latest structure, we will
5388 // immediately save to the new one, and remove the old.
5390 std::string sectordir1 = getSectorDir(p2d, 1);
5391 std::string sectordir;
5392 if(fs::PathExists(sectordir1))
5394 sectordir = sectordir1;
5399 sectordir = getSectorDir(p2d, 2);
5402 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5405 sector = loadSectorMeta(sectordir, loadlayout != 2);
5407 catch(InvalidFilenameException &e)
5411 catch(FileNotGoodException &e)
5415 catch(std::exception &e)
5423 std::vector<fs::DirListNode> list2 = fs::GetDirListing
5425 std::vector<fs::DirListNode>::iterator i2;
5426 for(i2=list2.begin(); i2!=list2.end(); i2++)
5432 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
5434 catch(InvalidFilenameException &e)
5436 // This catches unknown crap in directory
5442 dstream<<"Sector converted to new layout - deleting "<<
5443 sectordir1<<std::endl;
5444 fs::RecursiveDelete(sectordir1);
5451 void ServerMap::saveBlock(MapBlock *block)
5453 DSTACK(__FUNCTION_NAME);
5455 Dummy blocks are not written
5457 if(block->isDummy())
5459 /*v3s16 p = block->getPos();
5460 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
5461 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
5465 // Format used for writing
5466 u8 version = SER_FMT_VER_HIGHEST;
5468 v3s16 p3d = block->getPos();
5469 v2s16 p2d(p3d.X, p3d.Z);
5470 std::string dir = getSectorDir(p2d);
5474 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
5475 std::string fullpath = dir + "/" + cc;
5476 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5477 if(o.good() == false)
5478 throw FileNotGoodException("Cannot open block data");
5481 [0] u8 serialization version
5484 o.write((char*)&version, 1);
5487 block->serialize(o, version);
5489 // Write extra data stored on disk
5490 block->serializeDiskExtra(o, version);
5492 // We just wrote it to the disk so clear modified flag
5493 block->resetChangedFlag();
5496 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
5498 DSTACK(__FUNCTION_NAME);
5500 std::string fullpath = sectordir+"/"+blockfile;
5503 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5504 if(is.good() == false)
5505 throw FileNotGoodException("Cannot open block file");
5507 v3s16 p3d = getBlockPos(sectordir, blockfile);
5508 v2s16 p2d(p3d.X, p3d.Z);
5510 assert(sector->getPos() == p2d);
5512 u8 version = SER_FMT_VER_INVALID;
5513 is.read((char*)&version, 1);
5516 throw SerializationError("ServerMap::loadBlock(): Failed"
5517 " to read MapBlock version");
5519 /*u32 block_size = MapBlock::serializedLength(version);
5520 SharedBuffer<u8> data(block_size);
5521 is.read((char*)*data, block_size);*/
5523 // This will always return a sector because we're the server
5524 //MapSector *sector = emergeSector(p2d);
5526 MapBlock *block = NULL;
5527 bool created_new = false;
5529 block = sector->getBlockNoCreate(p3d.Y);
5531 catch(InvalidPositionException &e)
5533 block = sector->createBlankBlockNoInsert(p3d.Y);
5538 block->deSerialize(is, version);
5540 // Read extra data stored on disk
5541 block->deSerializeDiskExtra(is, version);
5543 // If it's a new block, insert it to the map
5545 sector->insertBlock(block);
5548 Save blocks loaded in old format in new format
5551 if(version < SER_FMT_VER_HIGHEST || save_after_load)
5556 // We just loaded it from the disk, so it's up-to-date.
5557 block->resetChangedFlag();
5560 catch(SerializationError &e)
5562 dstream<<"WARNING: Invalid block data on disk "
5563 "(SerializationError). Ignoring. "
5564 "A new one will be generated."
5567 // TODO: Backup file; name is in fullpath.
5571 void ServerMap::PrintInfo(std::ostream &out)
5582 ClientMap::ClientMap(
5584 MapDrawControl &control,
5585 scene::ISceneNode* parent,
5586 scene::ISceneManager* mgr,
5590 scene::ISceneNode(parent, mgr, id),
5593 m_camera_position(0,0,0),
5594 m_camera_direction(0,0,1)
5596 m_camera_mutex.Init();
5597 assert(m_camera_mutex.IsInitialized());
5599 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
5600 BS*1000000,BS*1000000,BS*1000000);
5603 ClientMap::~ClientMap()
5605 /*JMutexAutoLock lock(mesh_mutex);
5614 MapSector * ClientMap::emergeSector(v2s16 p2d)
5616 DSTACK(__FUNCTION_NAME);
5617 // Check that it doesn't exist already
5619 return getSectorNoGenerate(p2d);
5621 catch(InvalidPositionException &e)
5626 ClientMapSector *sector = new ClientMapSector(this, p2d);
5629 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5630 m_sectors.insert(p2d, sector);
5636 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
5638 DSTACK(__FUNCTION_NAME);
5639 ClientMapSector *sector = NULL;
5641 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5643 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
5647 sector = (ClientMapSector*)n->getValue();
5648 assert(sector->getId() == MAPSECTOR_CLIENT);
5652 sector = new ClientMapSector(this, p2d);
5654 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5655 m_sectors.insert(p2d, sector);
5659 sector->deSerialize(is);
5662 void ClientMap::OnRegisterSceneNode()
5666 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5667 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5670 ISceneNode::OnRegisterSceneNode();
5673 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5675 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5676 DSTACK(__FUNCTION_NAME);
5678 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5681 Get time for measuring timeout.
5683 Measuring time is very useful for long delays when the
5684 machine is swapping a lot.
5686 int time1 = time(0);
5688 //u32 daynight_ratio = m_client->getDayNightRatio();
5690 m_camera_mutex.Lock();
5691 v3f camera_position = m_camera_position;
5692 v3f camera_direction = m_camera_direction;
5693 m_camera_mutex.Unlock();
5696 Get all blocks and draw all visible ones
5699 v3s16 cam_pos_nodes(
5700 camera_position.X / BS,
5701 camera_position.Y / BS,
5702 camera_position.Z / BS);
5704 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5706 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5707 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5709 // Take a fair amount as we will be dropping more out later
5711 p_nodes_min.X / MAP_BLOCKSIZE - 1,
5712 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
5713 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
5715 p_nodes_max.X / MAP_BLOCKSIZE + 1,
5716 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
5717 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
5719 u32 vertex_count = 0;
5721 // For limiting number of mesh updates per frame
5722 u32 mesh_update_count = 0;
5724 u32 blocks_would_have_drawn = 0;
5725 u32 blocks_drawn = 0;
5727 //NOTE: The sectors map should be locked but we're not doing it
5728 // because it'd cause too much delays
5730 int timecheck_counter = 0;
5731 core::map<v2s16, MapSector*>::Iterator si;
5732 si = m_sectors.getIterator();
5733 for(; si.atEnd() == false; si++)
5736 timecheck_counter++;
5737 if(timecheck_counter > 50)
5739 timecheck_counter = 0;
5740 int time2 = time(0);
5741 if(time2 > time1 + 4)
5743 dstream<<"ClientMap::renderMap(): "
5744 "Rendering takes ages, returning."
5751 MapSector *sector = si.getNode()->getValue();
5752 v2s16 sp = sector->getPos();
5754 if(m_control.range_all == false)
5756 if(sp.X < p_blocks_min.X
5757 || sp.X > p_blocks_max.X
5758 || sp.Y < p_blocks_min.Z
5759 || sp.Y > p_blocks_max.Z)
5763 core::list< MapBlock * > sectorblocks;
5764 sector->getBlocks(sectorblocks);
5770 core::list< MapBlock * >::Iterator i;
5771 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5773 MapBlock *block = *i;
5776 Compare block position to camera position, skip
5777 if not seen on display
5780 float range = 100000 * BS;
5781 if(m_control.range_all == false)
5782 range = m_control.wanted_range * BS;
5785 if(isBlockInSight(block->getPos(), camera_position,
5786 camera_direction, range, &d) == false)
5791 // This is ugly (spherical distance limit?)
5792 /*if(m_control.range_all == false &&
5793 d - 0.5*BS*MAP_BLOCKSIZE > range)
5798 Update expired mesh (used for day/night change)
5800 It doesn't work exactly like it should now with the
5801 tasked mesh update but whatever.
5804 bool mesh_expired = false;
5807 JMutexAutoLock lock(block->mesh_mutex);
5809 mesh_expired = block->getMeshExpired();
5811 // Mesh has not been expired and there is no mesh:
5812 // block has no content
5813 if(block->mesh == NULL && mesh_expired == false)
5817 f32 faraway = BS*50;
5818 //f32 faraway = m_control.wanted_range * BS;
5821 This has to be done with the mesh_mutex unlocked
5823 // Pretty random but this should work somewhat nicely
5824 if(mesh_expired && (
5825 (mesh_update_count < 3
5826 && (d < faraway || mesh_update_count < 2)
5829 (m_control.range_all && mesh_update_count < 20)
5832 /*if(mesh_expired && mesh_update_count < 6
5833 && (d < faraway || mesh_update_count < 3))*/
5835 mesh_update_count++;
5837 // Mesh has been expired: generate new mesh
5838 //block->updateMesh(daynight_ratio);
5839 m_client->addUpdateMeshTask(block->getPos());
5841 mesh_expired = false;
5846 Draw the faces of the block
5849 JMutexAutoLock lock(block->mesh_mutex);
5851 scene::SMesh *mesh = block->mesh;
5856 blocks_would_have_drawn++;
5857 if(blocks_drawn >= m_control.wanted_max_blocks
5858 && m_control.range_all == false
5859 && d > m_control.wanted_min_range * BS)
5863 u32 c = mesh->getMeshBufferCount();
5865 for(u32 i=0; i<c; i++)
5867 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
5868 const video::SMaterial& material = buf->getMaterial();
5869 video::IMaterialRenderer* rnd =
5870 driver->getMaterialRenderer(material.MaterialType);
5871 bool transparent = (rnd && rnd->isTransparent());
5872 // Render transparent on transparent pass and likewise.
5873 if(transparent == is_transparent_pass)
5876 This *shouldn't* hurt too much because Irrlicht
5877 doesn't change opengl textures if the old
5878 material is set again.
5880 driver->setMaterial(buf->getMaterial());
5881 driver->drawMeshBuffer(buf);
5882 vertex_count += buf->getVertexCount();
5886 } // foreach sectorblocks
5889 m_control.blocks_drawn = blocks_drawn;
5890 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
5892 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
5893 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
5896 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
5897 core::map<v3s16, MapBlock*> *affected_blocks)
5899 bool changed = false;
5901 Add it to all blocks touching it
5904 v3s16(0,0,0), // this
5905 v3s16(0,0,1), // back
5906 v3s16(0,1,0), // top
5907 v3s16(1,0,0), // right
5908 v3s16(0,0,-1), // front
5909 v3s16(0,-1,0), // bottom
5910 v3s16(-1,0,0), // left
5912 for(u16 i=0; i<7; i++)
5914 v3s16 p2 = p + dirs[i];
5915 // Block position of neighbor (or requested) node
5916 v3s16 blockpos = getNodeBlockPos(p2);
5917 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5918 if(blockref == NULL)
5920 // Relative position of requested node
5921 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5922 if(blockref->setTempMod(relpos, mod))
5927 if(changed && affected_blocks!=NULL)
5929 for(u16 i=0; i<7; i++)
5931 v3s16 p2 = p + dirs[i];
5932 // Block position of neighbor (or requested) node
5933 v3s16 blockpos = getNodeBlockPos(p2);
5934 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5935 if(blockref == NULL)
5937 affected_blocks->insert(blockpos, blockref);
5943 bool ClientMap::clearTempMod(v3s16 p,
5944 core::map<v3s16, MapBlock*> *affected_blocks)
5946 bool changed = false;
5948 v3s16(0,0,0), // this
5949 v3s16(0,0,1), // back
5950 v3s16(0,1,0), // top
5951 v3s16(1,0,0), // right
5952 v3s16(0,0,-1), // front
5953 v3s16(0,-1,0), // bottom
5954 v3s16(-1,0,0), // left
5956 for(u16 i=0; i<7; i++)
5958 v3s16 p2 = p + dirs[i];
5959 // Block position of neighbor (or requested) node
5960 v3s16 blockpos = getNodeBlockPos(p2);
5961 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5962 if(blockref == NULL)
5964 // Relative position of requested node
5965 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5966 if(blockref->clearTempMod(relpos))
5971 if(changed && affected_blocks!=NULL)
5973 for(u16 i=0; i<7; i++)
5975 v3s16 p2 = p + dirs[i];
5976 // Block position of neighbor (or requested) node
5977 v3s16 blockpos = getNodeBlockPos(p2);
5978 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5979 if(blockref == NULL)
5981 affected_blocks->insert(blockpos, blockref);
5987 void ClientMap::expireMeshes(bool only_daynight_diffed)
5989 TimeTaker timer("expireMeshes()");
5991 core::map<v2s16, MapSector*>::Iterator si;
5992 si = m_sectors.getIterator();
5993 for(; si.atEnd() == false; si++)
5995 MapSector *sector = si.getNode()->getValue();
5997 core::list< MapBlock * > sectorblocks;
5998 sector->getBlocks(sectorblocks);
6000 core::list< MapBlock * >::Iterator i;
6001 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
6003 MapBlock *block = *i;
6005 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
6011 JMutexAutoLock lock(block->mesh_mutex);
6012 if(block->mesh != NULL)
6014 /*block->mesh->drop();
6015 block->mesh = NULL;*/
6016 block->setMeshExpired(true);
6023 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
6025 assert(mapType() == MAPTYPE_CLIENT);
6028 v3s16 p = blockpos + v3s16(0,0,0);
6029 MapBlock *b = getBlockNoCreate(p);
6030 b->updateMesh(daynight_ratio);
6031 //b->setMeshExpired(true);
6033 catch(InvalidPositionException &e){}
6036 v3s16 p = blockpos + v3s16(-1,0,0);
6037 MapBlock *b = getBlockNoCreate(p);
6038 b->updateMesh(daynight_ratio);
6039 //b->setMeshExpired(true);
6041 catch(InvalidPositionException &e){}
6043 v3s16 p = blockpos + v3s16(0,-1,0);
6044 MapBlock *b = getBlockNoCreate(p);
6045 b->updateMesh(daynight_ratio);
6046 //b->setMeshExpired(true);
6048 catch(InvalidPositionException &e){}
6050 v3s16 p = blockpos + v3s16(0,0,-1);
6051 MapBlock *b = getBlockNoCreate(p);
6052 b->updateMesh(daynight_ratio);
6053 //b->setMeshExpired(true);
6055 catch(InvalidPositionException &e){}
6060 Update mesh of block in which the node is, and if the node is at the
6061 leading edge, update the appropriate leading blocks too.
6063 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
6071 v3s16 blockposes[4];
6072 for(u32 i=0; i<4; i++)
6074 v3s16 np = nodepos + dirs[i];
6075 blockposes[i] = getNodeBlockPos(np);
6076 // Don't update mesh of block if it has been done already
6077 bool already_updated = false;
6078 for(u32 j=0; j<i; j++)
6080 if(blockposes[j] == blockposes[i])
6082 already_updated = true;
6089 MapBlock *b = getBlockNoCreate(blockposes[i]);
6090 b->updateMesh(daynight_ratio);
6095 void ClientMap::PrintInfo(std::ostream &out)
6106 MapVoxelManipulator::MapVoxelManipulator(Map *map)
6111 MapVoxelManipulator::~MapVoxelManipulator()
6113 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
6117 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
6119 TimeTaker timer1("emerge", &emerge_time);
6121 // Units of these are MapBlocks
6122 v3s16 p_min = getNodeBlockPos(a.MinEdge);
6123 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
6125 VoxelArea block_area_nodes
6126 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6128 addArea(block_area_nodes);
6130 for(s32 z=p_min.Z; z<=p_max.Z; z++)
6131 for(s32 y=p_min.Y; y<=p_max.Y; y++)
6132 for(s32 x=p_min.X; x<=p_max.X; x++)
6135 core::map<v3s16, bool>::Node *n;
6136 n = m_loaded_blocks.find(p);
6140 bool block_data_inexistent = false;
6143 TimeTaker timer1("emerge load", &emerge_load_time);
6145 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
6146 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
6149 dstream<<std::endl;*/
6151 MapBlock *block = m_map->getBlockNoCreate(p);
6152 if(block->isDummy())
6153 block_data_inexistent = true;
6155 block->copyTo(*this);
6157 catch(InvalidPositionException &e)
6159 block_data_inexistent = true;
6162 if(block_data_inexistent)
6164 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6165 // Fill with VOXELFLAG_INEXISTENT
6166 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
6167 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
6169 s32 i = m_area.index(a.MinEdge.X,y,z);
6170 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
6174 m_loaded_blocks.insert(p, !block_data_inexistent);
6177 //dstream<<"emerge done"<<std::endl;
6181 SUGG: Add an option to only update eg. water and air nodes.
6182 This will make it interfere less with important stuff if
6185 void MapVoxelManipulator::blitBack
6186 (core::map<v3s16, MapBlock*> & modified_blocks)
6188 if(m_area.getExtent() == v3s16(0,0,0))
6191 //TimeTaker timer1("blitBack");
6193 /*dstream<<"blitBack(): m_loaded_blocks.size()="
6194 <<m_loaded_blocks.size()<<std::endl;*/
6197 Initialize block cache
6199 v3s16 blockpos_last;
6200 MapBlock *block = NULL;
6201 bool block_checked_in_modified = false;
6203 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
6204 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
6205 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
6209 u8 f = m_flags[m_area.index(p)];
6210 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
6213 MapNode &n = m_data[m_area.index(p)];
6215 v3s16 blockpos = getNodeBlockPos(p);
6220 if(block == NULL || blockpos != blockpos_last){
6221 block = m_map->getBlockNoCreate(blockpos);
6222 blockpos_last = blockpos;
6223 block_checked_in_modified = false;
6226 // Calculate relative position in block
6227 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
6229 // Don't continue if nothing has changed here
6230 if(block->getNode(relpos) == n)
6233 //m_map->setNode(m_area.MinEdge + p, n);
6234 block->setNode(relpos, n);
6237 Make sure block is in modified_blocks
6239 if(block_checked_in_modified == false)
6241 modified_blocks[blockpos] = block;
6242 block_checked_in_modified = true;
6245 catch(InvalidPositionException &e)
6251 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
6252 MapVoxelManipulator(map),
6253 m_create_area(false)
6257 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
6261 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
6263 // Just create the area so that it can be pointed to
6264 VoxelManipulator::emerge(a, caller_id);
6267 void ManualMapVoxelManipulator::initialEmerge(
6268 v3s16 blockpos_min, v3s16 blockpos_max)
6270 TimeTaker timer1("initialEmerge", &emerge_time);
6272 // Units of these are MapBlocks
6273 v3s16 p_min = blockpos_min;
6274 v3s16 p_max = blockpos_max;
6276 VoxelArea block_area_nodes
6277 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6279 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
6282 dstream<<"initialEmerge: area: ";
6283 block_area_nodes.print(dstream);
6284 dstream<<" ("<<size_MB<<"MB)";
6288 addArea(block_area_nodes);
6290 for(s32 z=p_min.Z; z<=p_max.Z; z++)
6291 for(s32 y=p_min.Y; y<=p_max.Y; y++)
6292 for(s32 x=p_min.X; x<=p_max.X; x++)
6295 core::map<v3s16, bool>::Node *n;
6296 n = m_loaded_blocks.find(p);
6300 bool block_data_inexistent = false;
6303 TimeTaker timer1("emerge load", &emerge_load_time);
6305 MapBlock *block = m_map->getBlockNoCreate(p);
6306 if(block->isDummy())
6307 block_data_inexistent = true;
6309 block->copyTo(*this);
6311 catch(InvalidPositionException &e)
6313 block_data_inexistent = true;
6316 if(block_data_inexistent)
6319 Mark area inexistent
6321 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6322 // Fill with VOXELFLAG_INEXISTENT
6323 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
6324 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
6326 s32 i = m_area.index(a.MinEdge.X,y,z);
6327 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
6331 m_loaded_blocks.insert(p, !block_data_inexistent);
6335 void ManualMapVoxelManipulator::blitBackAll(
6336 core::map<v3s16, MapBlock*> * modified_blocks)
6338 if(m_area.getExtent() == v3s16(0,0,0))
6342 Copy data of all blocks
6344 for(core::map<v3s16, bool>::Iterator
6345 i = m_loaded_blocks.getIterator();
6346 i.atEnd() == false; i++)
6348 bool existed = i.getNode()->getValue();
6349 if(existed == false)
6351 v3s16 p = i.getNode()->getKey();
6352 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
6355 dstream<<"WARNING: "<<__FUNCTION_NAME
6356 <<": got NULL block "
6357 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
6362 block->copyFrom(*this);
6365 modified_blocks->insert(p, block);