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)
901 If the new node is mud and it is under sunlight, change it
904 if(n.d == CONTENT_MUD && node_under_sunlight)
910 Remove all light that has come out of this node
913 enum LightBank banks[] =
918 for(s32 i=0; i<2; i++)
920 enum LightBank bank = banks[i];
922 u8 lightwas = getNode(p).getLight(bank);
924 // Add the block of the added node to modified_blocks
925 v3s16 blockpos = getNodeBlockPos(p);
926 MapBlock * block = getBlockNoCreate(blockpos);
927 assert(block != NULL);
928 modified_blocks.insert(blockpos, block);
930 assert(isValidPosition(p));
932 // Unlight neighbours of node.
933 // This means setting light of all consequent dimmer nodes
935 // This also collects the nodes at the border which will spread
936 // light again into this.
937 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
943 If node lets sunlight through and is under sunlight, it has
946 if(node_under_sunlight && content_features(n.d).sunlight_propagates)
948 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
952 Set the node on the map
961 NodeMetadata *meta_proto = content_features(n.d).initial_metadata;
964 NodeMetadata *meta = meta_proto->clone();
965 setNodeMetadata(p, meta);
969 If node is under sunlight and doesn't let sunlight through,
970 take all sunlighted nodes under it and clear light from them
971 and from where the light has been spread.
972 TODO: This could be optimized by mass-unlighting instead
975 if(node_under_sunlight && !content_features(n.d).sunlight_propagates)
979 //m_dout<<DTIME<<"y="<<y<<std::endl;
980 v3s16 n2pos(p.X, y, p.Z);
986 catch(InvalidPositionException &e)
991 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
993 unLightNeighbors(LIGHTBANK_DAY,
994 n2pos, n2.getLight(LIGHTBANK_DAY),
995 light_sources, modified_blocks);
996 n2.setLight(LIGHTBANK_DAY, 0);
1004 for(s32 i=0; i<2; i++)
1006 enum LightBank bank = banks[i];
1009 Spread light from all nodes that might be capable of doing so
1011 spreadLight(bank, light_sources, modified_blocks);
1015 Update information about whether day and night light differ
1017 for(core::map<v3s16, MapBlock*>::Iterator
1018 i = modified_blocks.getIterator();
1019 i.atEnd() == false; i++)
1021 MapBlock *block = i.getNode()->getValue();
1022 block->updateDayNightDiff();
1026 Add neighboring liquid nodes and the node itself if it is
1027 liquid (=water node was added) to transform queue.
1030 v3s16(0,0,0), // self
1031 v3s16(0,0,1), // back
1032 v3s16(0,1,0), // top
1033 v3s16(1,0,0), // right
1034 v3s16(0,0,-1), // front
1035 v3s16(0,-1,0), // bottom
1036 v3s16(-1,0,0), // left
1038 for(u16 i=0; i<7; i++)
1043 v3s16 p2 = p + dirs[i];
1045 MapNode n2 = getNode(p2);
1046 if(content_liquid(n2.d))
1048 m_transforming_liquid.push_back(p2);
1051 }catch(InvalidPositionException &e)
1059 void Map::removeNodeAndUpdate(v3s16 p,
1060 core::map<v3s16, MapBlock*> &modified_blocks)
1062 /*PrintInfo(m_dout);
1063 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1064 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1066 bool node_under_sunlight = true;
1068 v3s16 toppos = p + v3s16(0,1,0);
1070 // Node will be replaced with this
1071 u8 replace_material = CONTENT_AIR;
1074 If there is a node at top and it doesn't have sunlight,
1075 there will be no sunlight going down.
1078 MapNode topnode = getNode(toppos);
1080 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1081 node_under_sunlight = false;
1083 catch(InvalidPositionException &e)
1087 core::map<v3s16, bool> light_sources;
1089 enum LightBank banks[] =
1094 for(s32 i=0; i<2; i++)
1096 enum LightBank bank = banks[i];
1099 Unlight neighbors (in case the node is a light source)
1101 unLightNeighbors(bank, p,
1102 getNode(p).getLight(bank),
1103 light_sources, modified_blocks);
1107 Remove node metadata
1110 removeNodeMetadata(p);
1114 This also clears the lighting.
1118 n.d = replace_material;
1121 for(s32 i=0; i<2; i++)
1123 enum LightBank bank = banks[i];
1126 Recalculate lighting
1128 spreadLight(bank, light_sources, modified_blocks);
1131 // Add the block of the removed node to modified_blocks
1132 v3s16 blockpos = getNodeBlockPos(p);
1133 MapBlock * block = getBlockNoCreate(blockpos);
1134 assert(block != NULL);
1135 modified_blocks.insert(blockpos, block);
1138 If the removed node was under sunlight, propagate the
1139 sunlight down from it and then light all neighbors
1140 of the propagated blocks.
1142 if(node_under_sunlight)
1144 s16 ybottom = propagateSunlight(p, modified_blocks);
1145 /*m_dout<<DTIME<<"Node was under sunlight. "
1146 "Propagating sunlight";
1147 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1149 for(; y >= ybottom; y--)
1151 v3s16 p2(p.X, y, p.Z);
1152 /*m_dout<<DTIME<<"lighting neighbors of node ("
1153 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1155 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1160 // Set the lighting of this node to 0
1161 // TODO: Is this needed? Lighting is cleared up there already.
1163 MapNode n = getNode(p);
1164 n.setLight(LIGHTBANK_DAY, 0);
1167 catch(InvalidPositionException &e)
1173 for(s32 i=0; i<2; i++)
1175 enum LightBank bank = banks[i];
1177 // Get the brightest neighbour node and propagate light from it
1178 v3s16 n2p = getBrightestNeighbour(bank, p);
1180 MapNode n2 = getNode(n2p);
1181 lightNeighbors(bank, n2p, modified_blocks);
1183 catch(InvalidPositionException &e)
1189 Update information about whether day and night light differ
1191 for(core::map<v3s16, MapBlock*>::Iterator
1192 i = modified_blocks.getIterator();
1193 i.atEnd() == false; i++)
1195 MapBlock *block = i.getNode()->getValue();
1196 block->updateDayNightDiff();
1200 Add neighboring liquid nodes to transform queue.
1203 v3s16(0,0,1), // back
1204 v3s16(0,1,0), // top
1205 v3s16(1,0,0), // right
1206 v3s16(0,0,-1), // front
1207 v3s16(0,-1,0), // bottom
1208 v3s16(-1,0,0), // left
1210 for(u16 i=0; i<6; i++)
1215 v3s16 p2 = p + dirs[i];
1217 MapNode n2 = getNode(p2);
1218 if(content_liquid(n2.d))
1220 m_transforming_liquid.push_back(p2);
1223 }catch(InvalidPositionException &e)
1229 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1232 event.type = MEET_ADDNODE;
1236 bool succeeded = true;
1238 core::map<v3s16, MapBlock*> modified_blocks;
1239 addNodeAndUpdate(p, n, modified_blocks);
1241 // Copy modified_blocks to event
1242 for(core::map<v3s16, MapBlock*>::Iterator
1243 i = modified_blocks.getIterator();
1244 i.atEnd()==false; i++)
1246 event.modified_blocks.insert(i.getNode()->getKey(), false);
1249 catch(InvalidPositionException &e){
1253 dispatchEvent(&event);
1258 bool Map::removeNodeWithEvent(v3s16 p)
1261 event.type = MEET_REMOVENODE;
1264 bool succeeded = true;
1266 core::map<v3s16, MapBlock*> modified_blocks;
1267 removeNodeAndUpdate(p, modified_blocks);
1269 // Copy modified_blocks to event
1270 for(core::map<v3s16, MapBlock*>::Iterator
1271 i = modified_blocks.getIterator();
1272 i.atEnd()==false; i++)
1274 event.modified_blocks.insert(i.getNode()->getKey(), false);
1277 catch(InvalidPositionException &e){
1281 dispatchEvent(&event);
1286 bool Map::dayNightDiffed(v3s16 blockpos)
1289 v3s16 p = blockpos + v3s16(0,0,0);
1290 MapBlock *b = getBlockNoCreate(p);
1291 if(b->dayNightDiffed())
1294 catch(InvalidPositionException &e){}
1297 v3s16 p = blockpos + v3s16(-1,0,0);
1298 MapBlock *b = getBlockNoCreate(p);
1299 if(b->dayNightDiffed())
1302 catch(InvalidPositionException &e){}
1304 v3s16 p = blockpos + v3s16(0,-1,0);
1305 MapBlock *b = getBlockNoCreate(p);
1306 if(b->dayNightDiffed())
1309 catch(InvalidPositionException &e){}
1311 v3s16 p = blockpos + v3s16(0,0,-1);
1312 MapBlock *b = getBlockNoCreate(p);
1313 if(b->dayNightDiffed())
1316 catch(InvalidPositionException &e){}
1319 v3s16 p = blockpos + v3s16(1,0,0);
1320 MapBlock *b = getBlockNoCreate(p);
1321 if(b->dayNightDiffed())
1324 catch(InvalidPositionException &e){}
1326 v3s16 p = blockpos + v3s16(0,1,0);
1327 MapBlock *b = getBlockNoCreate(p);
1328 if(b->dayNightDiffed())
1331 catch(InvalidPositionException &e){}
1333 v3s16 p = blockpos + v3s16(0,0,1);
1334 MapBlock *b = getBlockNoCreate(p);
1335 if(b->dayNightDiffed())
1338 catch(InvalidPositionException &e){}
1344 Updates usage timers
1346 void Map::timerUpdate(float dtime)
1348 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
1350 core::map<v2s16, MapSector*>::Iterator si;
1352 si = m_sectors.getIterator();
1353 for(; si.atEnd() == false; si++)
1355 MapSector *sector = si.getNode()->getValue();
1356 sector->usage_timer += dtime;
1360 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1363 Wait for caches to be removed before continuing.
1365 This disables the existence of caches while locked
1367 //SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
1369 core::list<v2s16>::Iterator j;
1370 for(j=list.begin(); j!=list.end(); j++)
1372 MapSector *sector = m_sectors[*j];
1375 sector->deleteBlocks();
1380 If sector is in sector cache, remove it from there
1382 if(m_sector_cache == sector)
1384 m_sector_cache = NULL;
1387 Remove from map and delete
1389 m_sectors.remove(*j);
1395 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1396 core::list<v3s16> *deleted_blocks)
1398 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
1400 core::list<v2s16> sector_deletion_queue;
1401 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1402 for(; i.atEnd() == false; i++)
1404 MapSector *sector = i.getNode()->getValue();
1406 Delete sector from memory if it hasn't been used in a long time
1408 if(sector->usage_timer > timeout)
1410 sector_deletion_queue.push_back(i.getNode()->getKey());
1412 if(deleted_blocks != NULL)
1414 // Collect positions of blocks of sector
1415 MapSector *sector = i.getNode()->getValue();
1416 core::list<MapBlock*> blocks;
1417 sector->getBlocks(blocks);
1418 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1419 i != blocks.end(); i++)
1421 deleted_blocks->push_back((*i)->getPos());
1426 deleteSectors(sector_deletion_queue, only_blocks);
1427 return sector_deletion_queue.getSize();
1430 void Map::PrintInfo(std::ostream &out)
1435 #define WATER_DROP_BOOST 4
1437 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1439 DSTACK(__FUNCTION_NAME);
1440 //TimeTaker timer("transformLiquids()");
1443 u32 initial_size = m_transforming_liquid.size();
1445 /*if(initial_size != 0)
1446 dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1448 while(m_transforming_liquid.size() != 0)
1451 Get a queued transforming liquid node
1453 v3s16 p0 = m_transforming_liquid.pop_front();
1455 MapNode n0 = getNode(p0);
1457 // Don't deal with non-liquids
1458 if(content_liquid(n0.d) == false)
1461 bool is_source = !content_flowing_liquid(n0.d);
1463 u8 liquid_level = 8;
1464 if(is_source == false)
1465 liquid_level = n0.param2 & 0x0f;
1467 // Turn possible source into non-source
1468 u8 nonsource_c = make_liquid_flowing(n0.d);
1471 If not source, check that some node flows into this one
1472 and what is the level of liquid in this one
1474 if(is_source == false)
1476 s8 new_liquid_level_max = -1;
1478 v3s16 dirs_from[5] = {
1479 v3s16(0,1,0), // top
1480 v3s16(0,0,1), // back
1481 v3s16(1,0,0), // right
1482 v3s16(0,0,-1), // front
1483 v3s16(-1,0,0), // left
1485 for(u16 i=0; i<5; i++)
1490 bool from_top = (i==0);
1492 v3s16 p2 = p0 + dirs_from[i];
1493 MapNode n2 = getNode(p2);
1495 if(content_liquid(n2.d))
1497 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1498 // Check that the liquids are the same type
1499 if(n2_nonsource_c != nonsource_c)
1501 dstream<<"WARNING: Not handling: different liquids"
1502 " collide"<<std::endl;
1505 bool n2_is_source = !content_flowing_liquid(n2.d);
1506 s8 n2_liquid_level = 8;
1507 if(n2_is_source == false)
1508 n2_liquid_level = n2.param2 & 0x07;
1510 s8 new_liquid_level = -1;
1513 //new_liquid_level = 7;
1514 if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
1515 new_liquid_level = 7;
1517 new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
1519 else if(n2_liquid_level > 0)
1521 new_liquid_level = n2_liquid_level - 1;
1524 if(new_liquid_level > new_liquid_level_max)
1525 new_liquid_level_max = new_liquid_level;
1528 }catch(InvalidPositionException &e)
1534 If liquid level should be something else, update it and
1535 add all the neighboring water nodes to the transform queue.
1537 if(new_liquid_level_max != liquid_level)
1539 if(new_liquid_level_max == -1)
1541 // Remove water alltoghether
1548 n0.param2 = new_liquid_level_max;
1552 // Block has been modified
1554 v3s16 blockpos = getNodeBlockPos(p0);
1555 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1557 modified_blocks.insert(blockpos, block);
1561 Add neighboring non-source liquid nodes to transform queue.
1564 v3s16(0,0,1), // back
1565 v3s16(0,1,0), // top
1566 v3s16(1,0,0), // right
1567 v3s16(0,0,-1), // front
1568 v3s16(0,-1,0), // bottom
1569 v3s16(-1,0,0), // left
1571 for(u16 i=0; i<6; i++)
1576 v3s16 p2 = p0 + dirs[i];
1578 MapNode n2 = getNode(p2);
1579 if(content_flowing_liquid(n2.d))
1581 m_transforming_liquid.push_back(p2);
1584 }catch(InvalidPositionException &e)
1591 // Get a new one from queue if the node has turned into non-water
1592 if(content_liquid(n0.d) == false)
1596 Flow water from this node
1598 v3s16 dirs_to[5] = {
1599 v3s16(0,-1,0), // bottom
1600 v3s16(0,0,1), // back
1601 v3s16(1,0,0), // right
1602 v3s16(0,0,-1), // front
1603 v3s16(-1,0,0), // left
1605 for(u16 i=0; i<5; i++)
1610 bool to_bottom = (i == 0);
1612 // If liquid is at lowest possible height, it's not going
1613 // anywhere except down
1614 if(liquid_level == 0 && to_bottom == false)
1617 u8 liquid_next_level = 0;
1618 // If going to bottom
1621 //liquid_next_level = 7;
1622 if(liquid_level >= 7 - WATER_DROP_BOOST)
1623 liquid_next_level = 7;
1625 liquid_next_level = liquid_level + WATER_DROP_BOOST;
1628 liquid_next_level = liquid_level - 1;
1630 bool n2_changed = false;
1631 bool flowed = false;
1633 v3s16 p2 = p0 + dirs_to[i];
1635 MapNode n2 = getNode(p2);
1636 //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
1638 if(content_liquid(n2.d))
1640 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1641 // Check that the liquids are the same type
1642 if(n2_nonsource_c != nonsource_c)
1644 dstream<<"WARNING: Not handling: different liquids"
1645 " collide"<<std::endl;
1648 bool n2_is_source = !content_flowing_liquid(n2.d);
1649 u8 n2_liquid_level = 8;
1650 if(n2_is_source == false)
1651 n2_liquid_level = n2.param2 & 0x07;
1660 // Just flow into the source, nothing changes.
1661 // n2_changed is not set because destination didn't change
1666 if(liquid_next_level > liquid_level)
1668 n2.param2 = liquid_next_level;
1676 else if(n2.d == CONTENT_AIR)
1679 n2.param2 = liquid_next_level;
1686 //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
1690 m_transforming_liquid.push_back(p2);
1692 v3s16 blockpos = getNodeBlockPos(p2);
1693 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1695 modified_blocks.insert(blockpos, block);
1698 // If n2_changed to bottom, don't flow anywhere else
1699 if(to_bottom && flowed && !is_source)
1702 }catch(InvalidPositionException &e)
1708 //if(loopcount >= 100000)
1709 if(loopcount >= initial_size * 1)
1712 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1715 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1717 v3s16 blockpos = getNodeBlockPos(p);
1718 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1719 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1722 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1726 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1730 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1732 v3s16 blockpos = getNodeBlockPos(p);
1733 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1734 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1737 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1741 block->m_node_metadata.set(p_rel, meta);
1744 void Map::removeNodeMetadata(v3s16 p)
1746 v3s16 blockpos = getNodeBlockPos(p);
1747 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1748 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1751 dstream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1755 block->m_node_metadata.remove(p_rel);
1758 void Map::nodeMetadataStep(float dtime,
1759 core::map<v3s16, MapBlock*> &changed_blocks)
1763 Currently there is no way to ensure that all the necessary
1764 blocks are loaded when this is run. (They might get unloaded)
1765 NOTE: ^- Actually, that might not be so. In a quick test it
1766 reloaded a block with a furnace when I walked back to it from
1769 core::map<v2s16, MapSector*>::Iterator si;
1770 si = m_sectors.getIterator();
1771 for(; si.atEnd() == false; si++)
1773 MapSector *sector = si.getNode()->getValue();
1774 core::list< MapBlock * > sectorblocks;
1775 sector->getBlocks(sectorblocks);
1776 core::list< MapBlock * >::Iterator i;
1777 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1779 MapBlock *block = *i;
1780 bool changed = block->m_node_metadata.step(dtime);
1782 changed_blocks[block->getPos()] = block;
1791 ServerMap::ServerMap(std::string savedir):
1794 m_map_metadata_changed(true)
1796 dstream<<__FUNCTION_NAME<<std::endl;
1799 //m_chunksize = 16; // Too slow
1800 m_chunksize = 8; // Takes a few seconds
1804 m_seed = (((u64)(myrand()%0xffff)<<0)
1805 + ((u64)(myrand()%0xffff)<<16)
1806 + ((u64)(myrand()%0xffff)<<32)
1807 + ((u64)(myrand()%0xffff)<<48));
1810 Experimental and debug stuff
1817 Try to load map; if not found, create a new one.
1820 m_savedir = savedir;
1821 m_map_saving_enabled = false;
1825 // If directory exists, check contents and load if possible
1826 if(fs::PathExists(m_savedir))
1828 // If directory is empty, it is safe to save into it.
1829 if(fs::GetDirListing(m_savedir).size() == 0)
1831 dstream<<DTIME<<"Server: Empty save directory is valid."
1833 m_map_saving_enabled = true;
1838 // Load map metadata (seed, chunksize)
1841 // Load chunk metadata
1844 catch(FileNotGoodException &e){
1845 dstream<<DTIME<<"WARNING: Server: Could not load "
1846 <<"metafile(s). Disabling chunk-based "
1847 <<"generation."<<std::endl;
1851 /*// Load sector (0,0) and throw and exception on fail
1852 if(loadSectorFull(v2s16(0,0)) == false)
1853 throw LoadError("Failed to load sector (0,0)");*/
1855 /*dstream<<DTIME<<"Server: Successfully loaded chunk "
1856 "metadata and sector (0,0) from "<<savedir<<
1857 ", assuming valid save directory."
1860 dstream<<DTIME<<"INFO: Server: Successfully loaded map "
1861 <<"and chunk metadata from "<<savedir
1862 <<", assuming valid save directory."
1865 m_map_saving_enabled = true;
1866 // Map loaded, not creating new one
1870 // If directory doesn't exist, it is safe to save to it
1872 m_map_saving_enabled = true;
1875 catch(std::exception &e)
1877 dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
1878 <<", exception: "<<e.what()<<std::endl;
1879 dstream<<"Please remove the map or fix it."<<std::endl;
1880 dstream<<"WARNING: Map saving will be disabled."<<std::endl;
1883 dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
1885 // Create zero sector
1886 emergeSector(v2s16(0,0));
1888 // Initially write whole map
1892 ServerMap::~ServerMap()
1894 dstream<<__FUNCTION_NAME<<std::endl;
1898 if(m_map_saving_enabled)
1901 // Save only changed parts
1903 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1907 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1910 catch(std::exception &e)
1912 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1913 <<", exception: "<<e.what()<<std::endl;
1919 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1920 for(; i.atEnd() == false; i++)
1922 MapChunk *chunk = i.getNode()->getValue();
1928 Some helper functions for the map generator
1931 s16 find_ground_level(VoxelManipulator &vmanip, v2s16 p2d)
1933 v3s16 em = vmanip.m_area.getExtent();
1934 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
1935 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
1936 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
1938 for(y=y_nodes_max; y>=y_nodes_min; y--)
1940 MapNode &n = vmanip.m_data[i];
1941 if(content_walkable(n.d))
1944 vmanip.m_area.add_y(em, i, -1);
1946 if(y >= y_nodes_min)
1952 s16 find_ground_level_clever(VoxelManipulator &vmanip, v2s16 p2d)
1954 v3s16 em = vmanip.m_area.getExtent();
1955 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
1956 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
1957 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
1959 for(y=y_nodes_max; y>=y_nodes_min; y--)
1961 MapNode &n = vmanip.m_data[i];
1962 if(content_walkable(n.d)
1963 && n.d != CONTENT_TREE
1964 && n.d != CONTENT_LEAVES)
1967 vmanip.m_area.add_y(em, i, -1);
1969 if(y >= y_nodes_min)
1975 void make_tree(VoxelManipulator &vmanip, v3s16 p0)
1977 MapNode treenode(CONTENT_TREE);
1978 MapNode leavesnode(CONTENT_LEAVES);
1980 s16 trunk_h = myrand_range(3, 6);
1982 for(s16 ii=0; ii<trunk_h; ii++)
1984 if(vmanip.m_area.contains(p1))
1985 vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
1989 // p1 is now the last piece of the trunk
1992 VoxelArea leaves_a(v3s16(-2,-2,-2), v3s16(2,2,2));
1993 //SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
1994 Buffer<u8> leaves_d(leaves_a.getVolume());
1995 for(s32 i=0; i<leaves_a.getVolume(); i++)
1998 // Force leaves at near the end of the trunk
2001 for(s16 z=-d; z<=d; z++)
2002 for(s16 y=-d; y<=d; y++)
2003 for(s16 x=-d; x<=d; x++)
2005 leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
2009 // Add leaves randomly
2010 for(u32 iii=0; iii<7; iii++)
2015 myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
2016 myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
2017 myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
2020 for(s16 z=0; z<=d; z++)
2021 for(s16 y=0; y<=d; y++)
2022 for(s16 x=0; x<=d; x++)
2024 leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
2028 // Blit leaves to vmanip
2029 for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
2030 for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
2031 for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
2035 if(vmanip.m_area.contains(p) == false)
2037 u32 vi = vmanip.m_area.index(p);
2038 if(vmanip.m_data[vi].d != CONTENT_AIR)
2040 u32 i = leaves_a.index(x,y,z);
2041 if(leaves_d[i] == 1)
2042 vmanip.m_data[vi] = leavesnode;
2047 Noise functions. Make sure seed is mangled differently in each one.
2050 // Amount of trees per area in nodes
2051 double tree_amount_2d(u64 seed, v2s16 p)
2053 double noise = noise2d_perlin(
2054 0.5+(float)p.X/250, 0.5+(float)p.Y/250,
2056 double zeroval = -0.3;
2060 return 0.04 * (noise-zeroval) / (1.0-zeroval);
2063 #define AVERAGE_MUD_AMOUNT 4
2065 double base_rock_level_2d(u64 seed, v2s16 p)
2067 // The base ground level
2068 double base = (double)WATER_LEVEL - (double)AVERAGE_MUD_AMOUNT
2069 + 20. * noise2d_perlin(
2070 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2071 (seed>>32)+654879876, 6, 0.6);
2073 /*// A bit hillier one
2074 double base2 = WATER_LEVEL - 4.0 + 40. * noise2d_perlin(
2075 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2076 (seed>>27)+90340, 6, 0.69);
2080 // Higher ground level
2081 double higher = (double)WATER_LEVEL + 25. + 35. * noise2d_perlin(
2082 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2083 seed+85039, 5, 0.69);
2084 //higher = 30; // For debugging
2086 // Limit higher to at least base
2090 // Steepness factor of cliffs
2091 double b = 1.0 + 1.0 * noise2d_perlin(
2092 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2094 b = rangelim(b, 0.0, 1000.0);
2097 b = rangelim(b, 3.0, 1000.0);
2098 //dstream<<"b="<<b<<std::endl;
2101 // Offset to more low
2102 double a_off = -0.2;
2103 // High/low selector
2104 /*double a = 0.5 + b * (a_off + noise2d_perlin(
2105 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2106 seed-359, 6, 0.7));*/
2107 double a = (double)0.5 + b * (a_off + noise2d_perlin(
2108 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2109 seed-359, 5, 0.60));
2111 a = rangelim(a, 0.0, 1.0);
2113 //dstream<<"a="<<a<<std::endl;
2115 double h = base*(1.0-a) + higher*a;
2122 double get_mud_add_amount(u64 seed, v2s16 p)
2124 return ((float)AVERAGE_MUD_AMOUNT + 3.0 * noise2d_perlin(
2125 0.5+(float)p.X/200, 0.5+(float)p.Y/200,
2126 seed+91013, 3, 0.55));
2130 Adds random objects to block, depending on the content of the block
2132 void addRandomObjects(MapBlock *block)
2134 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2135 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2137 bool last_node_walkable = false;
2138 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2141 MapNode n = block->getNodeNoEx(p);
2142 if(n.d == CONTENT_IGNORE)
2144 if(content_features(n.d).liquid_type != LIQUID_NONE)
2146 if(content_features(n.d).walkable)
2148 last_node_walkable = true;
2151 if(last_node_walkable)
2153 // If block contains light information
2154 if(content_features(n.d).param_type == CPT_LIGHT)
2156 if(n.getLight(LIGHTBANK_DAY) <= 3)
2158 if(myrand() % 300 == 0)
2160 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
2162 ServerActiveObject *obj = new RatSAO(NULL, 0, pos_f);
2163 std::string data = obj->getStaticData();
2164 StaticObject s_obj(obj->getType(),
2165 obj->getBasePosition(), data);
2167 block->m_static_objects.insert(0, s_obj);
2168 block->m_static_objects.insert(0, s_obj);
2169 block->m_static_objects.insert(0, s_obj);
2170 block->m_static_objects.insert(0, s_obj);
2171 block->m_static_objects.insert(0, s_obj);
2172 block->m_static_objects.insert(0, s_obj);
2175 if(myrand() % 300 == 0)
2177 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
2179 ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f);
2180 std::string data = obj->getStaticData();
2181 StaticObject s_obj(obj->getType(),
2182 obj->getBasePosition(), data);
2184 block->m_static_objects.insert(0, s_obj);
2190 last_node_walkable = false;
2193 block->setChangedFlag();
2196 #define VMANIP_FLAG_DUNGEON VOXELFLAG_CHECKED1
2199 This is the main map generation method
2202 void makeChunk(ChunkMakeData *data)
2207 s16 y_nodes_min = data->y_blocks_min * MAP_BLOCKSIZE;
2208 s16 y_nodes_max = data->y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
2209 s16 h_blocks = data->y_blocks_max - data->y_blocks_min + 1;
2210 u32 relative_volume = (u32)data->sectorpos_base_size*MAP_BLOCKSIZE
2211 *(u32)data->sectorpos_base_size*MAP_BLOCKSIZE
2212 *(u32)h_blocks*MAP_BLOCKSIZE;
2213 v3s16 bigarea_blocks_min(
2214 data->sectorpos_bigbase.X,
2216 data->sectorpos_bigbase.Y
2218 v3s16 bigarea_blocks_max(
2219 data->sectorpos_bigbase.X + data->sectorpos_bigbase_size - 1,
2221 data->sectorpos_bigbase.Y + data->sectorpos_bigbase_size - 1
2223 s16 lighting_min_d = 0-data->max_spread_amount;
2224 s16 lighting_max_d = data->sectorpos_base_size*MAP_BLOCKSIZE
2225 + data->max_spread_amount-1;
2228 data->vmanip.clearFlag(0xff);
2230 TimeTaker timer_generate("makeChunk() generate");
2232 // Maximum height of the stone surface and obstacles.
2233 // This is used to disable cave generation from going too high.
2234 s16 stone_surface_max_y = 0;
2237 Generate general ground level to full area
2241 TimeTaker timer1("Generating ground level");
2244 NoiseBuffer noisebuf1;
2245 //NoiseBuffer noisebuf2;
2248 data->sectorpos_bigbase.X*MAP_BLOCKSIZE,
2250 data->sectorpos_bigbase.Y*MAP_BLOCKSIZE
2252 v3f maxpos_f = minpos_f + v3f(
2253 data->sectorpos_bigbase_size*MAP_BLOCKSIZE,
2254 y_nodes_max-y_nodes_min,
2255 data->sectorpos_bigbase_size*MAP_BLOCKSIZE
2257 v3f samplelength_f = v3f(4.0, 4.0, 4.0);
2259 TimeTaker timer("noisebuf.create");
2261 noisebuf1.create(data->seed+25104, 6, 0.60, 200.0,
2262 minpos_f.X, minpos_f.Y, minpos_f.Z,
2263 maxpos_f.X, maxpos_f.Y, maxpos_f.Z,
2264 samplelength_f.X, samplelength_f.Y, samplelength_f.Z);
2265 /*noisebuf1.create(data->seed+25104, 3, 0.60, 25.0,
2266 minpos_f.X, minpos_f.Y, minpos_f.Z,
2267 maxpos_f.X, maxpos_f.Y, maxpos_f.Z,
2268 samplelength_f.X, samplelength_f.Y, samplelength_f.Z);
2269 noisebuf2.create(data->seed+25105, 4, 0.50, 200.0,
2270 minpos_f.X, minpos_f.Y, minpos_f.Z,
2271 maxpos_f.X, maxpos_f.Y, maxpos_f.Z,
2272 samplelength_f.X, samplelength_f.Y, samplelength_f.Z);*/
2275 for(s16 x=0; x<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2276 for(s16 z=0; z<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2279 v2s16 p2d = data->sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2281 // Ground height at this point
2282 float surface_y_f = 0.0;
2284 // Use perlin noise for ground height
2285 surface_y_f = base_rock_level_2d(data->seed, p2d);
2286 //surface_y_f = base_rock_level_2d(data->seed, p2d);
2288 // Convert to integer
2289 s16 surface_y = (s16)surface_y_f;
2292 if(surface_y > stone_surface_max_y)
2293 stone_surface_max_y = surface_y;
2296 Fill ground with stone
2299 // Use fast index incrementing
2300 v3s16 em = data->vmanip.m_area.getExtent();
2301 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2302 for(s16 y=y_nodes_min; y<=y_nodes_max; y++)
2304 // Skip if already generated.
2305 // This is done here because there might be a cave at
2306 // any point in ground, which could look like it
2307 // wasn't generated.
2308 if(data->vmanip.m_data[i].d != CONTENT_AIR)
2311 /*s16 noiseval = 50.0 * noise3d_perlin(
2312 0.5+(float)p2d.X/100.0,
2314 0.5+(float)p2d.Y/100.0,
2315 data->seed+123, 5, 0.5);*/
2316 double noiseval = 64.0 * noisebuf1.get(p2d.X, y, p2d.Y);
2317 /*double noiseval = 30.0 * noisebuf1.get(p2d.X, y, p2d.Y);
2318 noiseval *= MYMAX(0, -0.2 + noisebuf2.get(p2d.X, y, p2d.Y));*/
2320 //if(y < surface_y + noiseval)
2323 data->vmanip.m_data[i].d = CONTENT_STONE;
2325 data->vmanip.m_area.add_y(em, i, 1);
2332 for(s16 x=0; x<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2333 for(s16 z=0; z<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2336 v2s16 p2d = data->sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2339 Skip of already generated
2342 v3s16 p(p2d.X, y_nodes_min, p2d.Y);
2343 if(data->vmanip.m_data[data->vmanip.m_area.index(p)].d != CONTENT_AIR)
2347 // Ground height at this point
2348 float surface_y_f = 0.0;
2350 // Use perlin noise for ground height
2351 surface_y_f = base_rock_level_2d(data->seed, p2d);
2353 /*// Experimental stuff
2355 float a = highlands_level_2d(data->seed, p2d);
2360 // Convert to integer
2361 s16 surface_y = (s16)surface_y_f;
2364 if(surface_y > stone_surface_max_y)
2365 stone_surface_max_y = surface_y;
2368 Fill ground with stone
2371 // Use fast index incrementing
2372 v3s16 em = data->vmanip.m_area.getExtent();
2373 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2374 for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++)
2376 // Skip if already generated.
2377 // This is done here because there might be a cave at
2378 // any point in ground, which could look like it
2379 // wasn't generated.
2380 if(data->vmanip.m_data[i].d != CONTENT_AIR)
2383 data->vmanip.m_data[i].d = CONTENT_STONE;
2385 data->vmanip.m_area.add_y(em, i, 1);
2394 Randomize some parameters
2397 //s32 stone_obstacle_count = 0;
2398 /*s32 stone_obstacle_count =
2399 rangelim((1.0+noise2d(data->seed+897,
2400 data->sectorpos_base.X, data->sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2402 //s16 stone_obstacle_max_height = 0;
2403 /*s16 stone_obstacle_max_height =
2404 rangelim((1.0+noise2d(data->seed+5902,
2405 data->sectorpos_base.X, data->sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2408 Loop this part, it will make stuff look older and newer nicely
2410 const u32 age_loops = 2;
2411 for(u32 i_age=0; i_age<age_loops; i_age++)
2413 /******************************
2414 BEGINNING OF AGING LOOP
2415 ******************************/
2420 //TimeTaker timer1("caves");
2425 u32 caves_count = relative_volume / 400000;
2426 u32 bruises_count = relative_volume * stone_surface_max_y / 40000000;
2427 if(stone_surface_max_y < WATER_LEVEL)
2429 /*u32 caves_count = 0;
2430 u32 bruises_count = 0;*/
2431 for(u32 jj=0; jj<caves_count+bruises_count; jj++)
2433 s16 min_tunnel_diameter = 3;
2434 s16 max_tunnel_diameter = 5;
2435 u16 tunnel_routepoints = 20;
2437 v3f main_direction(0,0,0);
2439 bool bruise_surface = (jj > caves_count);
2443 min_tunnel_diameter = 5;
2444 max_tunnel_diameter = myrand_range(10, 20);
2445 /*min_tunnel_diameter = MYMAX(0, stone_surface_max_y/6);
2446 max_tunnel_diameter = myrand_range(MYMAX(0, stone_surface_max_y/6), MYMAX(0, stone_surface_max_y/2));*/
2448 /*s16 tunnel_rou = rangelim(25*(0.5+1.0*noise2d(data->seed+42,
2449 data->sectorpos_base.X, data->sectorpos_base.Y)), 0, 15);*/
2451 tunnel_routepoints = 5;
2457 // Allowed route area size in nodes
2459 data->sectorpos_base_size*MAP_BLOCKSIZE,
2460 h_blocks*MAP_BLOCKSIZE,
2461 data->sectorpos_base_size*MAP_BLOCKSIZE
2464 // Area starting point in nodes
2466 data->sectorpos_base.X*MAP_BLOCKSIZE,
2467 data->y_blocks_min*MAP_BLOCKSIZE,
2468 data->sectorpos_base.Y*MAP_BLOCKSIZE
2472 //(this should be more than the maximum radius of the tunnel)
2473 //s16 insure = 5; // Didn't work with max_d = 20
2475 s16 more = data->max_spread_amount - max_tunnel_diameter/2 - insure;
2476 ar += v3s16(1,0,1) * more * 2;
2477 of -= v3s16(1,0,1) * more;
2479 s16 route_y_min = 0;
2480 // Allow half a diameter + 7 over stone surface
2481 s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2 + 7;
2483 /*// If caves, don't go through surface too often
2484 if(bruise_surface == false)
2485 route_y_max -= myrand_range(0, max_tunnel_diameter*2);*/
2487 // Limit maximum to area
2488 route_y_max = rangelim(route_y_max, 0, ar.Y-1);
2492 /*// Minimum is at y=0
2493 route_y_min = -of.Y - 0;*/
2494 // Minimum is at y=max_tunnel_diameter/4
2495 //route_y_min = -of.Y + max_tunnel_diameter/4;
2496 //s16 min = -of.Y + max_tunnel_diameter/4;
2497 s16 min = -of.Y + 0;
2498 route_y_min = myrand_range(min, min + max_tunnel_diameter);
2499 route_y_min = rangelim(route_y_min, 0, route_y_max);
2502 /*dstream<<"route_y_min = "<<route_y_min
2503 <<", route_y_max = "<<route_y_max<<std::endl;*/
2505 s16 route_start_y_min = route_y_min;
2506 s16 route_start_y_max = route_y_max;
2508 // Start every 2nd cave from surface
2509 bool coming_from_surface = (jj % 2 == 0 && bruise_surface == false);
2511 if(coming_from_surface)
2513 route_start_y_min = -of.Y + stone_surface_max_y + 10;
2516 route_start_y_min = rangelim(route_start_y_min, 0, ar.Y-1);
2517 route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y-1);
2519 // Randomize starting position
2521 (float)(myrand()%ar.X)+0.5,
2522 (float)(myrand_range(route_start_y_min, route_start_y_max))+0.5,
2523 (float)(myrand()%ar.Z)+0.5
2526 MapNode airnode(CONTENT_AIR);
2529 Generate some tunnel starting from orp
2532 for(u16 j=0; j<tunnel_routepoints; j++)
2534 if(j%7==0 && bruise_surface == false)
2536 main_direction = v3f(
2537 ((float)(myrand()%20)-(float)10)/10,
2538 ((float)(myrand()%20)-(float)10)/30,
2539 ((float)(myrand()%20)-(float)10)/10
2541 main_direction *= (float)myrand_range(1, 3);
2545 s16 min_d = min_tunnel_diameter;
2546 s16 max_d = max_tunnel_diameter;
2547 s16 rs = myrand_range(min_d, max_d);
2552 maxlen = v3s16(rs*7,rs*7,rs*7);
2556 maxlen = v3s16(rs*4, myrand_range(1, rs*3), rs*4);
2561 if(coming_from_surface && j < 3)
2564 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2565 (float)(myrand()%(maxlen.Y*1))-(float)maxlen.Y,
2566 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2572 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2573 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2574 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2578 vec += main_direction;
2583 else if(rp.X >= ar.X)
2585 if(rp.Y < route_y_min)
2587 else if(rp.Y >= route_y_max)
2588 rp.Y = route_y_max-1;
2591 else if(rp.Z >= ar.Z)
2595 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2597 v3f fp = orp + vec * f;
2598 v3s16 cp(fp.X, fp.Y, fp.Z);
2601 s16 d1 = d0 + rs - 1;
2602 for(s16 z0=d0; z0<=d1; z0++)
2604 //s16 si = rs - MYMAX(0, abs(z0)-rs/4);
2605 s16 si = rs - MYMAX(0, abs(z0)-rs/7);
2606 for(s16 x0=-si; x0<=si-1; x0++)
2608 s16 maxabsxz = MYMAX(abs(x0), abs(z0));
2609 //s16 si2 = rs - MYMAX(0, maxabsxz-rs/4);
2610 s16 si2 = rs - MYMAX(0, maxabsxz-rs/7);
2611 //s16 si2 = rs - abs(x0);
2612 for(s16 y0=-si2+1+2; y0<=si2-1; y0++)
2618 /*if(isInArea(p, ar) == false)
2620 // Check only height
2621 if(y < 0 || y >= ar.Y)
2625 //assert(data->vmanip.m_area.contains(p));
2626 if(data->vmanip.m_area.contains(p) == false)
2628 dstream<<"WARNING: "<<__FUNCTION_NAME
2629 <<":"<<__LINE__<<": "
2630 <<"point not in area"
2635 // Just set it to air, it will be changed to
2637 u32 i = data->vmanip.m_area.index(p);
2638 data->vmanip.m_data[i] = airnode;
2640 if(bruise_surface == false)
2643 data->vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON;
2661 //TimeTaker timer1("ore veins");
2666 for(u32 jj=0; jj<relative_volume/1000; jj++)
2668 s16 max_vein_diameter = 3;
2670 // Allowed route area size in nodes
2672 data->sectorpos_base_size*MAP_BLOCKSIZE,
2673 h_blocks*MAP_BLOCKSIZE,
2674 data->sectorpos_base_size*MAP_BLOCKSIZE
2677 // Area starting point in nodes
2679 data->sectorpos_base.X*MAP_BLOCKSIZE,
2680 data->y_blocks_min*MAP_BLOCKSIZE,
2681 data->sectorpos_base.Y*MAP_BLOCKSIZE
2685 //(this should be more than the maximum radius of the tunnel)
2687 s16 more = data->max_spread_amount - max_vein_diameter/2 - insure;
2688 ar += v3s16(1,0,1) * more * 2;
2689 of -= v3s16(1,0,1) * more;
2691 // Randomize starting position
2693 (float)(myrand()%ar.X)+0.5,
2694 (float)(myrand()%ar.Y)+0.5,
2695 (float)(myrand()%ar.Z)+0.5
2698 // Randomize mineral
2701 mineral = MINERAL_COAL;
2703 mineral = MINERAL_IRON;
2706 Generate some vein starting from orp
2709 for(u16 j=0; j<2; j++)
2712 (float)(myrand()%ar.X)+0.5,
2713 (float)(myrand()%ar.Y)+0.5,
2714 (float)(myrand()%ar.Z)+0.5
2716 v3f vec = rp - orp;*/
2718 v3s16 maxlen(5, 5, 5);
2720 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2721 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2722 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2727 else if(rp.X >= ar.X)
2731 else if(rp.Y >= ar.Y)
2735 else if(rp.Z >= ar.Z)
2741 s16 max_d = max_vein_diameter;
2742 s16 rs = myrand_range(min_d, max_d);
2744 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2746 v3f fp = orp + vec * f;
2747 v3s16 cp(fp.X, fp.Y, fp.Z);
2749 s16 d1 = d0 + rs - 1;
2750 for(s16 z0=d0; z0<=d1; z0++)
2752 s16 si = rs - abs(z0);
2753 for(s16 x0=-si; x0<=si-1; x0++)
2755 s16 si2 = rs - abs(x0);
2756 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2758 // Don't put mineral to every place
2766 /*if(isInArea(p, ar) == false)
2768 // Check only height
2769 if(y < 0 || y >= ar.Y)
2773 assert(data->vmanip.m_area.contains(p));
2775 // Just set it to air, it will be changed to
2777 u32 i = data->vmanip.m_area.index(p);
2778 MapNode *n = &data->vmanip.m_data[i];
2779 if(n->d == CONTENT_STONE)
2797 TimeTaker timer1("add mud");
2800 Add mud to the central chunk
2803 for(s16 x=0; x<data->sectorpos_base_size*MAP_BLOCKSIZE; x++)
2804 for(s16 z=0; z<data->sectorpos_base_size*MAP_BLOCKSIZE; z++)
2806 // Node position in 2d
2807 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2809 // Randomize mud amount
2810 s16 mud_add_amount = get_mud_add_amount(data->seed, p2d) / 2.0;
2812 // Find ground level
2813 s16 surface_y = find_ground_level_clever(data->vmanip, p2d);
2816 If topmost node is grass, change it to mud.
2817 It might be if it was flown to there from a neighboring
2818 chunk and then converted.
2821 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y));
2822 MapNode *n = &data->vmanip.m_data[i];
2823 if(n->d == CONTENT_GRASS)
2824 *n = MapNode(CONTENT_MUD);
2825 //n->d = CONTENT_MUD;
2833 v3s16 em = data->vmanip.m_area.getExtent();
2834 s16 y_start = surface_y+1;
2835 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2836 for(s16 y=y_start; y<=y_nodes_max; y++)
2838 if(mudcount >= mud_add_amount)
2841 MapNode &n = data->vmanip.m_data[i];
2842 n = MapNode(CONTENT_MUD);
2843 //n.d = CONTENT_MUD;
2846 data->vmanip.m_area.add_y(em, i, 1);
2858 TimeTaker timer1("flow mud");
2861 Flow mud away from steep edges
2864 // Limit area by 1 because mud is flown into neighbors.
2865 s16 mudflow_minpos = 0-data->max_spread_amount+1;
2866 s16 mudflow_maxpos = data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-2;
2868 // Iterate a few times
2869 for(s16 k=0; k<3; k++)
2872 for(s16 x=mudflow_minpos;
2875 for(s16 z=mudflow_minpos;
2879 // Invert coordinates every 2nd iteration
2882 x = mudflow_maxpos - (x-mudflow_minpos);
2883 z = mudflow_maxpos - (z-mudflow_minpos);
2886 // Node position in 2d
2887 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2889 v3s16 em = data->vmanip.m_area.getExtent();
2890 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2897 for(; y>=y_nodes_min; y--)
2899 n = &data->vmanip.m_data[i];
2900 //if(content_walkable(n->d))
2902 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2905 data->vmanip.m_area.add_y(em, i, -1);
2908 // Stop if out of area
2909 //if(data->vmanip.m_area.contains(i) == false)
2913 /*// If not mud, do nothing to it
2914 MapNode *n = &data->vmanip.m_data[i];
2915 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
2919 Don't flow it if the stuff under it is not mud
2923 data->vmanip.m_area.add_y(em, i2, -1);
2924 // Cancel if out of area
2925 if(data->vmanip.m_area.contains(i2) == false)
2927 MapNode *n2 = &data->vmanip.m_data[i2];
2928 if(n2->d != CONTENT_MUD && n2->d != CONTENT_GRASS)
2932 // Make it exactly mud
2935 /*s16 recurse_count = 0;
2939 v3s16(0,0,1), // back
2940 v3s16(1,0,0), // right
2941 v3s16(0,0,-1), // front
2942 v3s16(-1,0,0), // left
2945 // Theck that upper is air or doesn't exist.
2946 // Cancel dropping if upper keeps it in place
2948 data->vmanip.m_area.add_y(em, i3, 1);
2949 if(data->vmanip.m_area.contains(i3) == true
2950 && content_walkable(data->vmanip.m_data[i3].d) == true)
2957 for(u32 di=0; di<4; di++)
2959 v3s16 dirp = dirs4[di];
2962 data->vmanip.m_area.add_p(em, i2, dirp);
2963 // Fail if out of area
2964 if(data->vmanip.m_area.contains(i2) == false)
2966 // Check that side is air
2967 MapNode *n2 = &data->vmanip.m_data[i2];
2968 if(content_walkable(n2->d))
2970 // Check that under side is air
2971 data->vmanip.m_area.add_y(em, i2, -1);
2972 if(data->vmanip.m_area.contains(i2) == false)
2974 n2 = &data->vmanip.m_data[i2];
2975 if(content_walkable(n2->d))
2977 /*// Check that under that is air (need a drop of 2)
2978 data->vmanip.m_area.add_y(em, i2, -1);
2979 if(data->vmanip.m_area.contains(i2) == false)
2981 n2 = &data->vmanip.m_data[i2];
2982 if(content_walkable(n2->d))
2984 // Loop further down until not air
2986 data->vmanip.m_area.add_y(em, i2, -1);
2987 // Fail if out of area
2988 if(data->vmanip.m_area.contains(i2) == false)
2990 n2 = &data->vmanip.m_data[i2];
2991 }while(content_walkable(n2->d) == false);
2992 // Loop one up so that we're in air
2993 data->vmanip.m_area.add_y(em, i2, 1);
2994 n2 = &data->vmanip.m_data[i2];
2996 // Move mud to new place
2998 // Set old place to be air
2999 *n = MapNode(CONTENT_AIR);
3015 TimeTaker timer1("add water");
3018 Add water to the central chunk (and a bit more)
3021 for(s16 x=0-data->max_spread_amount;
3022 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3024 for(s16 z=0-data->max_spread_amount;
3025 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3028 // Node position in 2d
3029 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3031 // Find ground level
3032 //s16 surface_y = find_ground_level(data->vmanip, p2d);
3035 If ground level is over water level, skip.
3036 NOTE: This leaves caves near water without water,
3037 which looks especially crappy when the nearby water
3038 won't start flowing either for some reason
3040 /*if(surface_y > WATER_LEVEL)
3047 v3s16 em = data->vmanip.m_area.getExtent();
3048 u8 light = LIGHT_MAX;
3049 // Start at global water surface level
3050 s16 y_start = WATER_LEVEL;
3051 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3052 MapNode *n = &data->vmanip.m_data[i];
3054 for(s16 y=y_start; y>=y_nodes_min; y--)
3056 n = &data->vmanip.m_data[i];
3058 // Stop when there is no water and no air
3059 if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
3060 && n->d != CONTENT_WATER)
3066 // Make water only not in caves
3067 if(!(data->vmanip.m_flags[i]&VMANIP_FLAG_DUNGEON))
3069 n->d = CONTENT_WATERSOURCE;
3070 //n->setLight(LIGHTBANK_DAY, light);
3072 // Add to transforming liquid queue (in case it'd
3074 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3075 data->transforming_liquid.push_back(p);
3079 data->vmanip.m_area.add_y(em, i, -1);
3091 /***********************
3093 ************************/
3097 //TimeTaker timer1("convert mud to sand");
3103 //s16 mud_add_amount = myrand_range(2, 4);
3104 //s16 mud_add_amount = 0;
3106 /*for(s16 x=0; x<data->sectorpos_base_size*MAP_BLOCKSIZE; x++)
3107 for(s16 z=0; z<data->sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3108 for(s16 x=0-data->max_spread_amount+1;
3109 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3111 for(s16 z=0-data->max_spread_amount+1;
3112 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3115 // Node position in 2d
3116 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3118 // Determine whether to have sand here
3119 double sandnoise = noise2d_perlin(
3120 0.5+(float)p2d.X/500, 0.5+(float)p2d.Y/500,
3121 data->seed+59420, 3, 0.50);
3123 bool have_sand = (sandnoise > -0.15);
3125 if(have_sand == false)
3128 // Find ground level
3129 s16 surface_y = find_ground_level_clever(data->vmanip, p2d);
3131 if(surface_y > WATER_LEVEL + 2)
3135 v3s16 em = data->vmanip.m_area.getExtent();
3136 s16 y_start = surface_y;
3137 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3138 u32 not_sand_counter = 0;
3139 for(s16 y=y_start; y>=y_nodes_min; y--)
3141 MapNode *n = &data->vmanip.m_data[i];
3142 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
3144 n->d = CONTENT_SAND;
3149 if(not_sand_counter > 3)
3153 data->vmanip.m_area.add_y(em, i, -1);
3165 //TimeTaker timer1("generate trees");
3171 // Divide area into parts
3173 s16 sidelen = data->sectorpos_base_size*MAP_BLOCKSIZE / div;
3174 double area = sidelen * sidelen;
3175 for(s16 x0=0; x0<div; x0++)
3176 for(s16 z0=0; z0<div; z0++)
3178 // Center position of part of division
3180 data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen/2 + sidelen*x0,
3181 data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen/2 + sidelen*z0
3183 // Minimum edge of part of division
3185 data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen*x0,
3186 data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen*z0
3188 // Maximum edge of part of division
3190 data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen + sidelen*x0 - 1,
3191 data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen + sidelen*z0 - 1
3194 u32 tree_count = area * tree_amount_2d(data->seed, p2d_center);
3195 // Put trees in random places on part of division
3196 for(u32 i=0; i<tree_count; i++)
3198 s16 x = myrand_range(p2d_min.X, p2d_max.X);
3199 s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
3200 s16 y = find_ground_level(data->vmanip, v2s16(x,z));
3201 // Don't make a tree under water level
3204 // Don't make a tree so high that it doesn't fit
3205 if(y > y_nodes_max - 6)
3209 Trees grow only on mud and grass
3212 u32 i = data->vmanip.m_area.index(v3s16(p));
3213 MapNode *n = &data->vmanip.m_data[i];
3214 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
3219 make_tree(data->vmanip, p);
3222 /*u32 tree_max = relative_area / 60;
3223 //u32 count = myrand_range(0, tree_max);
3224 for(u32 i=0; i<count; i++)
3226 s16 x = myrand_range(0, data->sectorpos_base_size*MAP_BLOCKSIZE-1);
3227 s16 z = myrand_range(0, data->sectorpos_base_size*MAP_BLOCKSIZE-1);
3228 x += data->sectorpos_base.X*MAP_BLOCKSIZE;
3229 z += data->sectorpos_base.Y*MAP_BLOCKSIZE;
3230 s16 y = find_ground_level(data->vmanip, v2s16(x,z));
3231 // Don't make a tree under water level
3236 make_tree(data->vmanip, p);
3246 //TimeTaker timer1("grow grass");
3252 /*for(s16 x=0-4; x<data->sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
3253 for(s16 z=0-4; z<data->sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
3254 for(s16 x=0-data->max_spread_amount;
3255 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3257 for(s16 z=0-data->max_spread_amount;
3258 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3261 // Node position in 2d
3262 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3265 Find the lowest surface to which enough light ends up
3268 Basically just wait until not air and not leaves.
3272 v3s16 em = data->vmanip.m_area.getExtent();
3273 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
3275 // Go to ground level
3276 for(y=y_nodes_max; y>=y_nodes_min; y--)
3278 MapNode &n = data->vmanip.m_data[i];
3279 if(n.d != CONTENT_AIR
3280 && n.d != CONTENT_LEAVES)
3282 data->vmanip.m_area.add_y(em, i, -1);
3284 if(y >= y_nodes_min)
3287 surface_y = y_nodes_min;
3290 u32 i = data->vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
3291 MapNode *n = &data->vmanip.m_data[i];
3292 if(n->d == CONTENT_MUD)
3293 n->d = CONTENT_GRASS;
3300 Initial lighting (sunlight)
3303 core::map<v3s16, bool> light_sources;
3306 // 750ms @cs=8, can't optimize more
3307 TimeTaker timer1("initial lighting");
3309 // NOTE: This is no used... umm... for some reason!
3312 Go through the edges and add all nodes that have light to light_sources
3316 for(s16 i=0; i<4; i++)
3318 for(s16 j=lighting_min_d;
3325 if(i == 0 || i == 1)
3327 x = (i==0) ? lighting_min_d : lighting_max_d;
3336 z = (i==0) ? lighting_min_d : lighting_max_d;
3343 // Node position in 2d
3344 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3347 v3s16 em = data->vmanip.m_area.getExtent();
3348 s16 y_start = y_nodes_max;
3349 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3350 for(s16 y=y_start; y>=y_nodes_min; y--)
3352 MapNode *n = &data->vmanip.m_data[i];
3353 if(n->getLight(LIGHTBANK_DAY) != 0)
3355 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3357 //NOTE: This is broken, at least the index has to
3366 Go through the edges and apply sunlight to them, not caring
3371 for(s16 i=0; i<4; i++)
3373 for(s16 j=lighting_min_d;
3380 if(i == 0 || i == 1)
3382 x = (i==0) ? lighting_min_d : lighting_max_d;
3391 z = (i==0) ? lighting_min_d : lighting_max_d;
3398 // Node position in 2d
3399 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3401 // Loop from top to down
3403 u8 light = LIGHT_SUN;
3404 v3s16 em = data->vmanip.m_area.getExtent();
3405 s16 y_start = y_nodes_max;
3406 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3407 for(s16 y=y_start; y>=y_nodes_min; y--)
3409 MapNode *n = &data->vmanip.m_data[i];
3410 if(light_propagates_content(n->d) == false)
3414 else if(light != LIGHT_SUN
3415 || sunlight_propagates_content(n->d) == false)
3421 n->setLight(LIGHTBANK_DAY, light);
3422 n->setLight(LIGHTBANK_NIGHT, 0);
3426 // Insert light source
3427 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3430 // Increment index by y
3431 data->vmanip.m_area.add_y(em, i, -1);
3437 /*for(s16 x=0; x<data->sectorpos_base_size*MAP_BLOCKSIZE; x++)
3438 for(s16 z=0; z<data->sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3439 /*for(s16 x=0-data->max_spread_amount+1;
3440 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3442 for(s16 z=0-data->max_spread_amount+1;
3443 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3447 This has to be 1 smaller than the actual area, because
3448 neighboring nodes are checked.
3450 for(s16 x=lighting_min_d+1;
3451 x<=lighting_max_d-1;
3453 for(s16 z=lighting_min_d+1;
3454 z<=lighting_max_d-1;
3457 // Node position in 2d
3458 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3461 Apply initial sunlight
3464 u8 light = LIGHT_SUN;
3465 bool add_to_sources = false;
3466 v3s16 em = data->vmanip.m_area.getExtent();
3467 s16 y_start = y_nodes_max;
3468 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3469 for(s16 y=y_start; y>=y_nodes_min; y--)
3471 MapNode *n = &data->vmanip.m_data[i];
3473 if(light_propagates_content(n->d) == false)
3477 else if(light != LIGHT_SUN
3478 || sunlight_propagates_content(n->d) == false)
3484 // This doesn't take much time
3485 if(add_to_sources == false)
3488 Check sides. If side is not air or water, start
3489 adding to light_sources.
3492 v3s16(0,0,1), // back
3493 v3s16(1,0,0), // right
3494 v3s16(0,0,-1), // front
3495 v3s16(-1,0,0), // left
3497 for(u32 di=0; di<4; di++)
3499 v3s16 dirp = dirs4[di];
3501 data->vmanip.m_area.add_p(em, i2, dirp);
3502 MapNode *n2 = &data->vmanip.m_data[i2];
3504 n2->d != CONTENT_AIR
3505 && n2->d != CONTENT_WATERSOURCE
3506 && n2->d != CONTENT_WATER
3508 add_to_sources = true;
3514 n->setLight(LIGHTBANK_DAY, light);
3515 n->setLight(LIGHTBANK_NIGHT, 0);
3517 // This doesn't take much time
3518 if(light != 0 && add_to_sources)
3520 // Insert light source
3521 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3524 // Increment index by y
3525 data->vmanip.m_area.add_y(em, i, -1);
3533 // Spread light around
3535 TimeTaker timer("makeChunk() spreadLight");
3536 data->vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
3543 timer_generate.stop();
3546 //###################################################################
3547 //###################################################################
3548 //###################################################################
3549 //###################################################################
3550 //###################################################################
3551 //###################################################################
3552 //###################################################################
3553 //###################################################################
3554 //###################################################################
3555 //###################################################################
3556 //###################################################################
3557 //###################################################################
3558 //###################################################################
3559 //###################################################################
3560 //###################################################################
3562 void ServerMap::initChunkMake(ChunkMakeData &data, v2s16 chunkpos)
3564 if(m_chunksize == 0)
3572 // The distance how far into the neighbors the generator is allowed to go.
3573 s16 max_spread_amount_sectors = 2;
3574 assert(max_spread_amount_sectors <= m_chunksize);
3575 s16 max_spread_amount = max_spread_amount_sectors * MAP_BLOCKSIZE;
3577 s16 y_blocks_min = -4;
3578 s16 y_blocks_max = 3;
3580 v2s16 sectorpos_base = chunk_to_sector(chunkpos);
3581 s16 sectorpos_base_size = m_chunksize;
3583 v2s16 sectorpos_bigbase =
3584 sectorpos_base - v2s16(1,1) * max_spread_amount_sectors;
3585 s16 sectorpos_bigbase_size =
3586 sectorpos_base_size + 2 * max_spread_amount_sectors;
3589 data.chunkpos = chunkpos;
3590 data.y_blocks_min = y_blocks_min;
3591 data.y_blocks_max = y_blocks_max;
3592 data.sectorpos_base = sectorpos_base;
3593 data.sectorpos_base_size = sectorpos_base_size;
3594 data.sectorpos_bigbase = sectorpos_bigbase;
3595 data.sectorpos_bigbase_size = sectorpos_bigbase_size;
3596 data.max_spread_amount = max_spread_amount;
3599 Create the whole area of this and the neighboring chunks
3602 TimeTaker timer("initChunkMake() create area");
3604 for(s16 x=0; x<sectorpos_bigbase_size; x++)
3605 for(s16 z=0; z<sectorpos_bigbase_size; z++)
3607 v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
3608 ServerMapSector *sector = createSector(sectorpos);
3611 for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
3613 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
3614 MapBlock *block = createBlock(blockpos);
3616 // Lighting won't be calculated
3617 //block->setLightingExpired(true);
3618 // Lighting will be calculated
3619 block->setLightingExpired(false);
3622 Block gets sunlight if this is true.
3624 This should be set to true when the top side of a block
3625 is completely exposed to the sky.
3627 Actually this doesn't matter now because the
3628 initial lighting is done here.
3630 block->setIsUnderground(y != y_blocks_max);
3636 Now we have a big empty area.
3638 Make a ManualMapVoxelManipulator that contains this and the
3642 v3s16 bigarea_blocks_min(
3643 sectorpos_bigbase.X,
3647 v3s16 bigarea_blocks_max(
3648 sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
3650 sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
3653 data.vmanip.setMap(this);
3656 TimeTaker timer("initChunkMake() initialEmerge");
3657 data.vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
3662 MapChunk* ServerMap::finishChunkMake(ChunkMakeData &data,
3663 core::map<v3s16, MapBlock*> &changed_blocks)
3669 Blit generated stuff to map
3673 //TimeTaker timer("generateChunkRaw() blitBackAll");
3674 data.vmanip.blitBackAll(&changed_blocks);
3678 Update day/night difference cache of the MapBlocks
3681 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
3682 i.atEnd() == false; i++)
3684 MapBlock *block = i.getNode()->getValue();
3685 block->updateDayNightDiff();
3690 Copy transforming liquid information
3692 while(data.transforming_liquid.size() > 0)
3694 v3s16 p = data.transforming_liquid.pop_front();
3695 m_transforming_liquid.push_back(p);
3699 Add random objects to blocks
3702 for(s16 x=0; x<data.sectorpos_base_size; x++)
3703 for(s16 z=0; z<data.sectorpos_base_size; z++)
3705 v2s16 sectorpos = data.sectorpos_base + v2s16(x,z);
3706 ServerMapSector *sector = createSector(sectorpos);
3709 for(s16 y=data.y_blocks_min; y<=data.y_blocks_max; y++)
3711 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
3712 MapBlock *block = createBlock(blockpos);
3713 addRandomObjects(block);
3719 Create chunk metadata
3722 for(s16 x=-1; x<=1; x++)
3723 for(s16 y=-1; y<=1; y++)
3725 v2s16 chunkpos0 = data.chunkpos + v2s16(x,y);
3726 // Add chunk meta information
3727 MapChunk *chunk = getChunk(chunkpos0);
3730 chunk = new MapChunk();
3731 m_chunks.insert(chunkpos0, chunk);
3733 //chunk->setIsVolatile(true);
3734 if(chunk->getGenLevel() > GENERATED_PARTLY)
3735 chunk->setGenLevel(GENERATED_PARTLY);
3739 Set central chunk non-volatile
3741 MapChunk *chunk = getChunk(data.chunkpos);
3744 //chunk->setIsVolatile(false);
3745 chunk->setGenLevel(GENERATED_FULLY);
3748 Save changed parts of map
3757 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
3758 core::map<v3s16, MapBlock*> &changed_blocks,
3761 DSTACK(__FUNCTION_NAME);
3764 Don't generate if already fully generated
3768 MapChunk *chunk = getChunk(chunkpos);
3769 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3771 dstream<<"generateChunkRaw(): Chunk "
3772 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
3773 <<" already generated"<<std::endl;
3778 dstream<<"generateChunkRaw(): Generating chunk "
3779 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
3782 TimeTaker timer("generateChunkRaw()");
3786 // Initialize generation
3787 initChunkMake(data, chunkpos);
3792 // Finalize generation
3793 MapChunk *chunk = finishChunkMake(data, changed_blocks);
3796 Return central chunk (which was requested)
3802 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
3803 core::map<v3s16, MapBlock*> &changed_blocks)
3805 dstream<<"generateChunk(): Generating chunk "
3806 <<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
3809 /*for(s16 x=-1; x<=1; x++)
3810 for(s16 y=-1; y<=1; y++)*/
3811 for(s16 x=-0; x<=0; x++)
3812 for(s16 y=-0; y<=0; y++)
3814 v2s16 chunkpos0 = chunkpos1 + v2s16(x,y);
3815 MapChunk *chunk = getChunk(chunkpos0);
3816 // Skip if already generated
3817 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3819 generateChunkRaw(chunkpos0, changed_blocks);
3822 assert(chunkNonVolatile(chunkpos1));
3824 MapChunk *chunk = getChunk(chunkpos1);
3829 ServerMapSector * ServerMap::createSector(v2s16 p2d)
3831 DSTACKF("%s: p2d=(%d,%d)",
3836 Check if it exists already in memory
3838 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3843 Try to load it from disk (with blocks)
3845 if(loadSectorFull(p2d) == true)
3847 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3850 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
3851 throw InvalidPositionException("");
3857 Do not create over-limit
3859 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3860 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3861 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3862 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3863 throw InvalidPositionException("createSector(): pos. over limit");
3866 Generate blank sector
3869 sector = new ServerMapSector(this, p2d);
3871 // Sector position on map in nodes
3872 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3877 m_sectors.insert(p2d, sector);
3883 MapSector * ServerMap::emergeSector(v2s16 p2d,
3884 core::map<v3s16, MapBlock*> &changed_blocks)
3886 DSTACK("%s: p2d=(%d,%d)",
3893 v2s16 chunkpos = sector_to_chunk(p2d);
3894 /*bool chunk_nonvolatile = false;
3895 MapChunk *chunk = getChunk(chunkpos);
3896 if(chunk && chunk->getIsVolatile() == false)
3897 chunk_nonvolatile = true;*/
3898 bool chunk_nonvolatile = chunkNonVolatile(chunkpos);
3901 If chunk is not fully generated, generate chunk
3903 if(chunk_nonvolatile == false)
3905 // Generate chunk and neighbors
3906 generateChunk(chunkpos, changed_blocks);
3910 Return sector if it exists now
3912 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3917 Try to load it from disk
3919 if(loadSectorFull(p2d) == true)
3921 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3924 dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
3925 throw InvalidPositionException("");
3931 generateChunk should have generated the sector
3935 dstream<<"WARNING: ServerMap::emergeSector: Cannot find sector ("
3936 <<p2d.X<<","<<p2d.Y<<" and chunk is already generated. "
3940 dstream<<"WARNING: Creating an empty sector."<<std::endl;
3942 return createSector(p2d);
3947 dstream<<"WARNING: Forcing regeneration of chunk."<<std::endl;
3950 generateChunkRaw(chunkpos, changed_blocks, true);
3953 Return sector if it exists now
3955 sector = getSectorNoGenerateNoEx(p2d);
3959 dstream<<"ERROR: Could not get sector from anywhere."<<std::endl;
3967 //return generateSector();
3972 NOTE: This is not used for main map generation, only for blocks
3973 that are very high or low
3975 MapBlock * ServerMap::generateBlock(
3977 MapBlock *original_dummy,
3978 ServerMapSector *sector,
3979 core::map<v3s16, MapBlock*> &changed_blocks,
3980 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
3983 DSTACKF("%s: p=(%d,%d,%d)",
3987 // If chunks are disabled
3988 /*if(m_chunksize == 0)
3990 dstream<<"ServerMap::generateBlock(): Chunks disabled -> "
3991 <<"not generating."<<std::endl;
3995 /*dstream<<"generateBlock(): "
3996 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3999 MapBlock *block = original_dummy;
4001 v2s16 p2d(p.X, p.Z);
4003 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
4006 Do not generate over-limit
4008 if(blockpos_over_limit(p))
4010 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
4011 throw InvalidPositionException("generateBlock(): pos. over limit");
4015 If block doesn't exist, create one.
4016 If it exists, it is a dummy. In that case unDummify() it.
4018 NOTE: This already sets the map as the parent of the block
4022 block = sector->createBlankBlockNoInsert(block_y);
4026 // Remove the block so that nobody can get a half-generated one.
4027 sector->removeBlock(block);
4028 // Allocate the block to contain the generated data
4034 Generate a completely empty block
4036 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4037 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4039 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4043 block->setNode(v3s16(x0,y0,z0), n);
4048 Generate a proper block
4051 u8 water_material = CONTENT_WATERSOURCE;
4053 s32 lowest_ground_y = 32767;
4054 s32 highest_ground_y = -32768;
4056 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4057 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4059 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
4061 //s16 surface_y = 0;
4063 s16 mud_add_amount = get_mud_add_amount(m_seed, p2d_nodes+v2s16(x0,z0));
4065 s16 surface_y = base_rock_level_2d(m_seed, p2d_nodes+v2s16(x0,z0))
4067 // If chunks are disabled
4068 if(m_chunksize == 0)
4069 surface_y = WATER_LEVEL + 1;
4071 if(surface_y < lowest_ground_y)
4072 lowest_ground_y = surface_y;
4073 if(surface_y > highest_ground_y)
4074 highest_ground_y = surface_y;
4076 s32 surface_depth = AVERAGE_MUD_AMOUNT;
4078 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4080 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
4085 NOTE: If there are some man-made structures above the
4086 newly created block, they won't be taken into account.
4088 if(real_y > surface_y)
4089 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
4095 // If node is over heightmap y, it's air or water
4096 if(real_y > surface_y)
4098 // If under water level, it's water
4099 if(real_y < WATER_LEVEL)
4101 n.d = water_material;
4102 n.setLight(LIGHTBANK_DAY,
4103 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
4105 Add to transforming liquid queue (in case it'd
4108 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
4109 m_transforming_liquid.push_back(real_pos);
4115 // Else it's ground or caves (air)
4118 // If it's surface_depth under ground, it's stone
4119 if(real_y <= surface_y - surface_depth)
4121 n.d = CONTENT_STONE;
4125 // It is mud if it is under the first ground
4126 // level or under water
4127 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
4133 n.d = CONTENT_GRASS;
4136 //n.d = CONTENT_MUD;
4138 /*// If under water level, it's mud
4139 if(real_y < WATER_LEVEL)
4141 // Only the topmost node is grass
4142 else if(real_y <= surface_y - 1)
4145 n.d = CONTENT_GRASS;*/
4149 block->setNode(v3s16(x0,y0,z0), n);
4154 Calculate some helper variables
4157 // Completely underground if the highest part of block is under lowest
4159 // This has to be very sure; it's probably one too strict now but
4160 // that's just better.
4161 bool completely_underground =
4162 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
4164 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
4166 bool mostly_underwater_surface = false;
4167 if(highest_ground_y < WATER_LEVEL
4168 && some_part_underground && !completely_underground)
4169 mostly_underwater_surface = true;
4172 Get local attributes
4175 //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
4177 float caves_amount = 0.5;
4182 NOTE: BEWARE: Too big amount of attribute points slows verything
4184 1 interpolation from 5000 points takes 2-3ms.
4186 //TimeTaker timer("generateBlock() local attribute retrieval");
4187 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
4188 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
4189 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
4193 //dstream<<"generateBlock(): Done"<<std::endl;
4199 // Initialize temporary table
4200 const s32 ued = MAP_BLOCKSIZE;
4201 bool underground_emptiness[ued*ued*ued];
4202 for(s32 i=0; i<ued*ued*ued; i++)
4204 underground_emptiness[i] = 0;
4211 Initialize orp and ors. Try to find if some neighboring
4212 MapBlock has a tunnel ended in its side
4216 (float)(myrand()%ued)+0.5,
4217 (float)(myrand()%ued)+0.5,
4218 (float)(myrand()%ued)+0.5
4221 bool found_existing = false;
4227 for(s16 y=0; y<ued; y++)
4228 for(s16 x=0; x<ued; x++)
4230 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4231 if(getNode(ap).d == CONTENT_AIR)
4233 orp = v3f(x+1,y+1,0);
4234 found_existing = true;
4235 goto continue_generating;
4239 catch(InvalidPositionException &e){}
4245 for(s16 y=0; y<ued; y++)
4246 for(s16 x=0; x<ued; x++)
4248 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4249 if(getNode(ap).d == CONTENT_AIR)
4251 orp = v3f(x+1,y+1,ued-1);
4252 found_existing = true;
4253 goto continue_generating;
4257 catch(InvalidPositionException &e){}
4263 for(s16 y=0; y<ued; y++)
4264 for(s16 z=0; z<ued; z++)
4266 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4267 if(getNode(ap).d == CONTENT_AIR)
4269 orp = v3f(0,y+1,z+1);
4270 found_existing = true;
4271 goto continue_generating;
4275 catch(InvalidPositionException &e){}
4281 for(s16 y=0; y<ued; y++)
4282 for(s16 z=0; z<ued; z++)
4284 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4285 if(getNode(ap).d == CONTENT_AIR)
4287 orp = v3f(ued-1,y+1,z+1);
4288 found_existing = true;
4289 goto continue_generating;
4293 catch(InvalidPositionException &e){}
4299 for(s16 x=0; x<ued; x++)
4300 for(s16 z=0; z<ued; z++)
4302 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4303 if(getNode(ap).d == CONTENT_AIR)
4305 orp = v3f(x+1,0,z+1);
4306 found_existing = true;
4307 goto continue_generating;
4311 catch(InvalidPositionException &e){}
4317 for(s16 x=0; x<ued; x++)
4318 for(s16 z=0; z<ued; z++)
4320 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4321 if(getNode(ap).d == CONTENT_AIR)
4323 orp = v3f(x+1,ued-1,z+1);
4324 found_existing = true;
4325 goto continue_generating;
4329 catch(InvalidPositionException &e){}
4331 continue_generating:
4334 Choose whether to actually generate cave
4336 bool do_generate_caves = true;
4337 // Don't generate if no part is underground
4338 if(!some_part_underground)
4340 do_generate_caves = false;
4342 // Don't generate if mostly underwater surface
4343 /*else if(mostly_underwater_surface)
4345 do_generate_caves = false;
4347 // Partly underground = cave
4348 else if(!completely_underground)
4350 do_generate_caves = (rand() % 100 <= (s32)(caves_amount*100));
4352 // Found existing cave underground
4353 else if(found_existing && completely_underground)
4355 do_generate_caves = (rand() % 100 <= (s32)(caves_amount*100));
4357 // Underground and no caves found
4360 do_generate_caves = (rand() % 300 <= (s32)(caves_amount*100));
4363 if(do_generate_caves)
4366 Generate some tunnel starting from orp and ors
4368 for(u16 i=0; i<3; i++)
4371 (float)(myrand()%ued)+0.5,
4372 (float)(myrand()%ued)+0.5,
4373 (float)(myrand()%ued)+0.5
4377 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
4381 for(float f=0; f<1.0; f+=0.04)
4383 v3f fp = orp + vec * f;
4384 v3s16 cp(fp.X, fp.Y, fp.Z);
4386 s16 d1 = d0 + rs - 1;
4387 for(s16 z0=d0; z0<=d1; z0++)
4389 s16 si = rs - abs(z0);
4390 for(s16 x0=-si; x0<=si-1; x0++)
4392 s16 si2 = rs - abs(x0);
4393 for(s16 y0=-si2+1; y0<=si2-1; y0++)
4399 if(isInArea(p, ued) == false)
4401 underground_emptiness[ued*ued*z + ued*y + x] = 1;
4413 // Set to true if has caves.
4414 // Set when some non-air is changed to air when making caves.
4415 bool has_caves = false;
4418 Apply temporary cave data to block
4421 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4422 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4424 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4426 MapNode n = block->getNode(v3s16(x0,y0,z0));
4429 if(underground_emptiness[
4430 ued*ued*(z0*ued/MAP_BLOCKSIZE)
4431 +ued*(y0*ued/MAP_BLOCKSIZE)
4432 +(x0*ued/MAP_BLOCKSIZE)])
4434 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
4443 block->setNode(v3s16(x0,y0,z0), n);
4448 This is used for guessing whether or not the block should
4449 receive sunlight from the top if the block above doesn't exist
4451 block->setIsUnderground(completely_underground);
4454 Force lighting update if some part of block is partly
4455 underground and has caves.
4457 /*if(some_part_underground && !completely_underground && has_caves)
4459 //dstream<<"Half-ground caves"<<std::endl;
4460 lighting_invalidated_blocks[block->getPos()] = block;
4463 // DEBUG: Always update lighting
4464 //lighting_invalidated_blocks[block->getPos()] = block;
4470 if(some_part_underground)
4472 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
4477 for(s16 i=0; i<underground_level/4 + 1; i++)
4479 if(myrand()%50 == 0)
4482 (myrand()%(MAP_BLOCKSIZE-2))+1,
4483 (myrand()%(MAP_BLOCKSIZE-2))+1,
4484 (myrand()%(MAP_BLOCKSIZE-2))+1
4490 for(u16 i=0; i<27; i++)
4492 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4494 block->setNode(cp+g_27dirs[i], n);
4502 u16 coal_amount = 30;
4503 u16 coal_rareness = 60 / coal_amount;
4504 if(coal_rareness == 0)
4506 if(myrand()%coal_rareness == 0)
4508 u16 a = myrand() % 16;
4509 u16 amount = coal_amount * a*a*a / 1000;
4510 for(s16 i=0; i<amount; i++)
4513 (myrand()%(MAP_BLOCKSIZE-2))+1,
4514 (myrand()%(MAP_BLOCKSIZE-2))+1,
4515 (myrand()%(MAP_BLOCKSIZE-2))+1
4519 n.d = CONTENT_STONE;
4520 n.param = MINERAL_COAL;
4522 for(u16 i=0; i<27; i++)
4524 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4526 block->setNode(cp+g_27dirs[i], n);
4534 //TODO: change to iron_amount or whatever
4535 u16 iron_amount = 15;
4536 u16 iron_rareness = 60 / iron_amount;
4537 if(iron_rareness == 0)
4539 if(myrand()%iron_rareness == 0)
4541 u16 a = myrand() % 16;
4542 u16 amount = iron_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_IRON;
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);
4566 Create a few rats in empty blocks underground
4568 if(completely_underground)
4570 //for(u16 i=0; i<2; i++)
4573 (myrand()%(MAP_BLOCKSIZE-2))+1,
4574 (myrand()%(MAP_BLOCKSIZE-2))+1,
4575 (myrand()%(MAP_BLOCKSIZE-2))+1
4578 // Check that the place is empty
4579 //if(!is_ground_content(block->getNode(cp).d))
4582 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp, BS));
4583 block->addObject(obj);
4588 #endif // end of proper block generation
4591 Add block to sector.
4593 sector->insertBlock(block);
4595 // Lighting is invalid after generation.
4596 block->setLightingExpired(true);
4603 <<"lighting_invalidated_blocks.size()"
4607 <<" "<<lighting_invalidated_blocks.size()
4609 <<", "<<completely_underground
4610 <<", "<<some_part_underground
4617 MapBlock * ServerMap::createBlock(v3s16 p)
4619 DSTACKF("%s: p=(%d,%d,%d)",
4620 __FUNCTION_NAME, p.X, p.Y, p.Z);
4623 Do not create over-limit
4625 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4626 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4627 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4628 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4629 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4630 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4631 throw InvalidPositionException("createBlock(): pos. over limit");
4633 v2s16 p2d(p.X, p.Z);
4636 This will create or load a sector if not found in memory.
4637 If block exists on disk, it will be loaded.
4639 NOTE: On old save formats, this will be slow, as it generates
4640 lighting on blocks for them.
4642 ServerMapSector *sector;
4644 sector = (ServerMapSector*)createSector(p2d);
4645 assert(sector->getId() == MAPSECTOR_SERVER);
4647 catch(InvalidPositionException &e)
4649 dstream<<"createBlock: createSector() failed"<<std::endl;
4653 NOTE: This should not be done, or at least the exception
4654 should not be passed on as std::exception, because it
4655 won't be catched at all.
4657 /*catch(std::exception &e)
4659 dstream<<"createBlock: createSector() failed: "
4660 <<e.what()<<std::endl;
4665 Try to get a block from the sector
4668 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4672 block = sector->createBlankBlock(block_y);
4676 MapBlock * ServerMap::emergeBlock(
4678 bool only_from_disk,
4679 core::map<v3s16, MapBlock*> &changed_blocks,
4680 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4683 DSTACKF("%s: p=(%d,%d,%d), only_from_disk=%d",
4685 p.X, p.Y, p.Z, only_from_disk);
4688 Do not generate over-limit
4690 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4691 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4692 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4693 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4694 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4695 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4696 throw InvalidPositionException("emergeBlock(): pos. over limit");
4698 v2s16 p2d(p.X, p.Z);
4701 This will create or load a sector if not found in memory.
4702 If block exists on disk, it will be loaded.
4704 ServerMapSector *sector;
4706 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
4707 assert(sector->getId() == MAPSECTOR_SERVER);
4709 catch(InvalidPositionException &e)
4711 dstream<<"emergeBlock: emergeSector() failed: "
4712 <<e.what()<<std::endl;
4713 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4715 <<"You could try to delete it."<<std::endl;
4718 catch(VersionMismatchException &e)
4720 dstream<<"emergeBlock: emergeSector() failed: "
4721 <<e.what()<<std::endl;
4722 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4724 <<"You could try to delete it."<<std::endl;
4728 NOTE: This should not be done, or at least the exception
4729 should not be passed on as std::exception, because it
4730 won't be catched at all.
4732 /*catch(std::exception &e)
4734 dstream<<"emergeBlock: emergeSector() failed: "
4735 <<e.what()<<std::endl;
4736 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4738 <<"You could try to delete it."<<std::endl;
4743 Try to get a block from the sector
4746 bool does_not_exist = false;
4747 bool lighting_expired = false;
4748 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4752 does_not_exist = true;
4754 else if(block->isDummy() == true)
4756 does_not_exist = true;
4758 else if(block->getLightingExpired())
4760 lighting_expired = true;
4765 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
4770 If block was not found on disk and not going to generate a
4771 new one, make sure there is a dummy block in place.
4773 if(only_from_disk && (does_not_exist || lighting_expired))
4775 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
4779 // Create dummy block
4780 block = new MapBlock(this, p, true);
4782 // Add block to sector
4783 sector->insertBlock(block);
4789 //dstream<<"Not found on disk, generating."<<std::endl;
4791 //TimeTaker("emergeBlock() generate");
4793 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
4796 If the block doesn't exist, generate the block.
4800 block = generateBlock(p, block, sector, changed_blocks,
4801 lighting_invalidated_blocks);
4804 if(lighting_expired)
4806 lighting_invalidated_blocks.insert(p, block);
4810 Initially update sunlight
4814 core::map<v3s16, bool> light_sources;
4815 bool black_air_left = false;
4816 bool bottom_invalid =
4817 block->propagateSunlight(light_sources, true,
4818 &black_air_left, true);
4820 // If sunlight didn't reach everywhere and part of block is
4821 // above ground, lighting has to be properly updated
4822 //if(black_air_left && some_part_underground)
4825 lighting_invalidated_blocks[block->getPos()] = block;
4830 lighting_invalidated_blocks[block->getPos()] = block;
4837 s16 ServerMap::findGroundLevel(v2s16 p2d)
4840 Uh, just do something random...
4842 // Find existing map from top to down
4845 v3s16 p(p2d.X, max, p2d.Y);
4846 for(; p.Y>min; p.Y--)
4848 MapNode n = getNodeNoEx(p);
4849 if(n.d != CONTENT_IGNORE)
4854 // If this node is not air, go to plan b
4855 if(getNodeNoEx(p).d != CONTENT_AIR)
4857 // Search existing walkable and return it
4858 for(; p.Y>min; p.Y--)
4860 MapNode n = getNodeNoEx(p);
4861 if(content_walkable(n.d) && n.d != CONTENT_IGNORE)
4867 Plan B: Get from map generator perlin noise function
4869 // This won't work if proper generation is disabled
4870 if(m_chunksize == 0)
4871 return WATER_LEVEL+2;
4872 double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
4876 void ServerMap::createDirs(std::string path)
4878 if(fs::CreateAllDirs(path) == false)
4880 m_dout<<DTIME<<"ServerMap: Failed to create directory "
4881 <<"\""<<path<<"\""<<std::endl;
4882 throw BaseException("ServerMap failed to create directory");
4886 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
4892 snprintf(cc, 9, "%.4x%.4x",
4893 (unsigned int)pos.X&0xffff,
4894 (unsigned int)pos.Y&0xffff);
4896 return m_savedir + "/sectors/" + cc;
4898 snprintf(cc, 9, "%.3x/%.3x",
4899 (unsigned int)pos.X&0xfff,
4900 (unsigned int)pos.Y&0xfff);
4902 return m_savedir + "/sectors2/" + cc;
4908 v2s16 ServerMap::getSectorPos(std::string dirname)
4912 size_t spos = dirname.rfind('/') + 1;
4913 assert(spos != std::string::npos);
4914 if(dirname.size() - spos == 8)
4917 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
4919 else if(dirname.size() - spos == 3)
4922 r = sscanf(dirname.substr(spos-4).c_str(), "%3x/%3x", &x, &y);
4923 // Sign-extend the 12 bit values up to 16 bits...
4924 if(x&0x800) x|=0xF000;
4925 if(y&0x800) y|=0xF000;
4932 v2s16 pos((s16)x, (s16)y);
4936 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
4938 v2s16 p2d = getSectorPos(sectordir);
4940 if(blockfile.size() != 4){
4941 throw InvalidFilenameException("Invalid block filename");
4944 int r = sscanf(blockfile.c_str(), "%4x", &y);
4946 throw InvalidFilenameException("Invalid block filename");
4947 return v3s16(p2d.X, y, p2d.Y);
4950 void ServerMap::save(bool only_changed)
4952 DSTACK(__FUNCTION_NAME);
4953 if(m_map_saving_enabled == false)
4955 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
4959 if(only_changed == false)
4960 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
4963 if(only_changed == false || m_map_metadata_changed)
4968 // Disable saving chunk metadata if chunks are disabled
4969 if(m_chunksize != 0)
4971 if(only_changed == false || anyChunkModified())
4975 u32 sector_meta_count = 0;
4976 u32 block_count = 0;
4979 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
4981 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
4982 for(; i.atEnd() == false; i++)
4984 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
4985 assert(sector->getId() == MAPSECTOR_SERVER);
4987 if(sector->differs_from_disk || only_changed == false)
4989 saveSectorMeta(sector);
4990 sector_meta_count++;
4992 core::list<MapBlock*> blocks;
4993 sector->getBlocks(blocks);
4994 core::list<MapBlock*>::Iterator j;
4995 for(j=blocks.begin(); j!=blocks.end(); j++)
4997 MapBlock *block = *j;
4998 if(block->getChangedFlag() || only_changed == false)
5003 /*dstream<<"ServerMap: Written block ("
5004 <<block->getPos().X<<","
5005 <<block->getPos().Y<<","
5006 <<block->getPos().Z<<")"
5015 Only print if something happened or saved whole map
5017 if(only_changed == false || sector_meta_count != 0
5018 || block_count != 0)
5020 dstream<<DTIME<<"ServerMap: Written: "
5021 <<sector_meta_count<<" sector metadata files, "
5022 <<block_count<<" block files"
5028 // NOTE: Doing this is insane. Deprecated and probably broken.
5029 void ServerMap::loadAll()
5031 DSTACK(__FUNCTION_NAME);
5032 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
5037 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
5039 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
5041 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5044 s32 printed_counter = -100000;
5045 s32 count = list.size();
5047 std::vector<fs::DirListNode>::iterator i;
5048 for(i=list.begin(); i!=list.end(); i++)
5050 if(counter > printed_counter + 10)
5052 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
5053 printed_counter = counter;
5057 MapSector *sector = NULL;
5059 // We want directories
5063 sector = loadSectorMeta(i->name);
5065 catch(InvalidFilenameException &e)
5067 // This catches unknown crap in directory
5070 std::vector<fs::DirListNode> list2 = fs::GetDirListing
5071 (m_savedir+"/sectors/"+i->name);
5072 std::vector<fs::DirListNode>::iterator i2;
5073 for(i2=list2.begin(); i2!=list2.end(); i2++)
5079 loadBlock(i->name, i2->name, sector);
5081 catch(InvalidFilenameException &e)
5083 // This catches unknown crap in directory
5087 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
5092 void ServerMap::saveMasterHeightmap()
5094 DSTACK(__FUNCTION_NAME);
5096 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
5098 createDir(m_savedir);
5100 /*std::string fullpath = m_savedir + "/master_heightmap";
5101 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5102 if(o.good() == false)
5103 throw FileNotGoodException("Cannot open master heightmap");*/
5105 // Format used for writing
5106 //u8 version = SER_FMT_VER_HIGHEST;
5109 void ServerMap::loadMasterHeightmap()
5111 DSTACK(__FUNCTION_NAME);
5113 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
5115 /*std::string fullpath = m_savedir + "/master_heightmap";
5116 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5117 if(is.good() == false)
5118 throw FileNotGoodException("Cannot open master heightmap");*/
5122 void ServerMap::saveMapMeta()
5124 DSTACK(__FUNCTION_NAME);
5126 dstream<<"INFO: ServerMap::saveMapMeta(): "
5127 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
5130 createDirs(m_savedir);
5132 std::string fullpath = m_savedir + "/map_meta.txt";
5133 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
5134 if(os.good() == false)
5136 dstream<<"ERROR: ServerMap::saveMapMeta(): "
5137 <<"could not open"<<fullpath<<std::endl;
5138 throw FileNotGoodException("Cannot open chunk metadata");
5142 params.setU64("seed", m_seed);
5143 params.setS32("chunksize", m_chunksize);
5145 params.writeLines(os);
5147 os<<"[end_of_params]\n";
5149 m_map_metadata_changed = false;
5152 void ServerMap::loadMapMeta()
5154 DSTACK(__FUNCTION_NAME);
5156 dstream<<"INFO: ServerMap::loadMapMeta(): Loading map metadata"
5159 std::string fullpath = m_savedir + "/map_meta.txt";
5160 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5161 if(is.good() == false)
5163 dstream<<"ERROR: ServerMap::loadMapMeta(): "
5164 <<"could not open"<<fullpath<<std::endl;
5165 throw FileNotGoodException("Cannot open map metadata");
5173 throw SerializationError
5174 ("ServerMap::loadMapMeta(): [end_of_params] not found");
5176 std::getline(is, line);
5177 std::string trimmedline = trim(line);
5178 if(trimmedline == "[end_of_params]")
5180 params.parseConfigLine(line);
5183 m_seed = params.getU64("seed");
5184 m_chunksize = params.getS32("chunksize");
5186 dstream<<"INFO: ServerMap::loadMapMeta(): "
5187 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
5191 void ServerMap::saveChunkMeta()
5193 DSTACK(__FUNCTION_NAME);
5195 // This should not be called if chunks are disabled.
5196 assert(m_chunksize != 0);
5198 u32 count = m_chunks.size();
5200 dstream<<"INFO: ServerMap::saveChunkMeta(): Saving metadata of "
5201 <<count<<" chunks"<<std::endl;
5203 createDirs(m_savedir);
5205 std::string fullpath = m_savedir + "/chunk_meta";
5206 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
5207 if(os.good() == false)
5209 dstream<<"ERROR: ServerMap::saveChunkMeta(): "
5210 <<"could not open"<<fullpath<<std::endl;
5211 throw FileNotGoodException("Cannot open chunk metadata");
5217 os.write((char*)&version, 1);
5222 writeU32(buf, count);
5223 os.write((char*)buf, 4);
5225 for(core::map<v2s16, MapChunk*>::Iterator
5226 i = m_chunks.getIterator();
5227 i.atEnd()==false; i++)
5229 v2s16 p = i.getNode()->getKey();
5230 MapChunk *chunk = i.getNode()->getValue();
5233 os.write((char*)buf, 4);
5235 chunk->serialize(os, version);
5238 setChunksNonModified();
5241 void ServerMap::loadChunkMeta()
5243 DSTACK(__FUNCTION_NAME);
5245 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading chunk metadata"
5248 std::string fullpath = m_savedir + "/chunk_meta";
5249 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5250 if(is.good() == false)
5252 dstream<<"ERROR: ServerMap::loadChunkMeta(): "
5253 <<"could not open"<<fullpath<<std::endl;
5254 throw FileNotGoodException("Cannot open chunk metadata");
5260 is.read((char*)&version, 1);
5265 is.read((char*)buf, 4);
5266 u32 count = readU32(buf);
5268 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading metadata of "
5269 <<count<<" chunks"<<std::endl;
5271 for(u32 i=0; i<count; i++)
5274 MapChunk *chunk = new MapChunk();
5276 is.read((char*)buf, 4);
5279 chunk->deSerialize(is, version);
5280 m_chunks.insert(p, chunk);
5284 void ServerMap::saveSectorMeta(ServerMapSector *sector)
5286 DSTACK(__FUNCTION_NAME);
5287 // Format used for writing
5288 u8 version = SER_FMT_VER_HIGHEST;
5290 v2s16 pos = sector->getPos();
5291 std::string dir = getSectorDir(pos);
5294 std::string fullpath = dir + "/meta";
5295 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5296 if(o.good() == false)
5297 throw FileNotGoodException("Cannot open sector metafile");
5299 sector->serialize(o, version);
5301 sector->differs_from_disk = false;
5304 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
5306 DSTACK(__FUNCTION_NAME);
5308 v2s16 p2d = getSectorPos(sectordir);
5310 ServerMapSector *sector = NULL;
5312 std::string fullpath = sectordir + "/meta";
5313 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5314 if(is.good() == false)
5316 // If the directory exists anyway, it probably is in some old
5317 // format. Just go ahead and create the sector.
5318 if(fs::PathExists(sectordir))
5320 dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
5321 <<fullpath<<" doesn't exist but directory does."
5322 <<" Continuing with a sector with no metadata."
5324 sector = new ServerMapSector(this, p2d);
5325 m_sectors.insert(p2d, sector);
5329 throw FileNotGoodException("Cannot open sector metafile");
5334 sector = ServerMapSector::deSerialize
5335 (is, this, p2d, m_sectors);
5337 saveSectorMeta(sector);
5340 sector->differs_from_disk = false;
5345 bool ServerMap::loadSectorFull(v2s16 p2d)
5347 DSTACK(__FUNCTION_NAME);
5349 MapSector *sector = NULL;
5351 // The directory layout we're going to load from.
5352 // 1 - original sectors/xxxxzzzz/
5353 // 2 - new sectors2/xxx/zzz/
5354 // If we load from anything but the latest structure, we will
5355 // immediately save to the new one, and remove the old.
5357 std::string sectordir1 = getSectorDir(p2d, 1);
5358 std::string sectordir;
5359 if(fs::PathExists(sectordir1))
5361 sectordir = sectordir1;
5366 sectordir = getSectorDir(p2d, 2);
5369 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5372 sector = loadSectorMeta(sectordir, loadlayout != 2);
5374 catch(InvalidFilenameException &e)
5378 catch(FileNotGoodException &e)
5382 catch(std::exception &e)
5390 std::vector<fs::DirListNode> list2 = fs::GetDirListing
5392 std::vector<fs::DirListNode>::iterator i2;
5393 for(i2=list2.begin(); i2!=list2.end(); i2++)
5399 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
5401 catch(InvalidFilenameException &e)
5403 // This catches unknown crap in directory
5409 dstream<<"Sector converted to new layout - deleting "<<
5410 sectordir1<<std::endl;
5411 fs::RecursiveDelete(sectordir1);
5418 void ServerMap::saveBlock(MapBlock *block)
5420 DSTACK(__FUNCTION_NAME);
5422 Dummy blocks are not written
5424 if(block->isDummy())
5426 /*v3s16 p = block->getPos();
5427 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
5428 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
5432 // Format used for writing
5433 u8 version = SER_FMT_VER_HIGHEST;
5435 v3s16 p3d = block->getPos();
5436 v2s16 p2d(p3d.X, p3d.Z);
5437 std::string dir = getSectorDir(p2d);
5441 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
5442 std::string fullpath = dir + "/" + cc;
5443 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5444 if(o.good() == false)
5445 throw FileNotGoodException("Cannot open block data");
5448 [0] u8 serialization version
5451 o.write((char*)&version, 1);
5454 block->serialize(o, version);
5456 // Write extra data stored on disk
5457 block->serializeDiskExtra(o, version);
5459 // We just wrote it to the disk so clear modified flag
5460 block->resetChangedFlag();
5463 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
5465 DSTACK(__FUNCTION_NAME);
5467 std::string fullpath = sectordir+"/"+blockfile;
5470 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5471 if(is.good() == false)
5472 throw FileNotGoodException("Cannot open block file");
5474 v3s16 p3d = getBlockPos(sectordir, blockfile);
5475 v2s16 p2d(p3d.X, p3d.Z);
5477 assert(sector->getPos() == p2d);
5479 u8 version = SER_FMT_VER_INVALID;
5480 is.read((char*)&version, 1);
5483 throw SerializationError("ServerMap::loadBlock(): Failed"
5484 " to read MapBlock version");
5486 /*u32 block_size = MapBlock::serializedLength(version);
5487 SharedBuffer<u8> data(block_size);
5488 is.read((char*)*data, block_size);*/
5490 // This will always return a sector because we're the server
5491 //MapSector *sector = emergeSector(p2d);
5493 MapBlock *block = NULL;
5494 bool created_new = false;
5496 block = sector->getBlockNoCreate(p3d.Y);
5498 catch(InvalidPositionException &e)
5500 block = sector->createBlankBlockNoInsert(p3d.Y);
5505 block->deSerialize(is, version);
5507 // Read extra data stored on disk
5508 block->deSerializeDiskExtra(is, version);
5510 // If it's a new block, insert it to the map
5512 sector->insertBlock(block);
5515 Save blocks loaded in old format in new format
5518 if(version < SER_FMT_VER_HIGHEST || save_after_load)
5523 // We just loaded it from the disk, so it's up-to-date.
5524 block->resetChangedFlag();
5527 catch(SerializationError &e)
5529 dstream<<"WARNING: Invalid block data on disk "
5530 "(SerializationError). Ignoring. "
5531 "A new one will be generated."
5534 // TODO: Backup file; name is in fullpath.
5538 void ServerMap::PrintInfo(std::ostream &out)
5549 ClientMap::ClientMap(
5551 MapDrawControl &control,
5552 scene::ISceneNode* parent,
5553 scene::ISceneManager* mgr,
5557 scene::ISceneNode(parent, mgr, id),
5560 m_camera_position(0,0,0),
5561 m_camera_direction(0,0,1)
5563 m_camera_mutex.Init();
5564 assert(m_camera_mutex.IsInitialized());
5566 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
5567 BS*1000000,BS*1000000,BS*1000000);
5570 ClientMap::~ClientMap()
5572 /*JMutexAutoLock lock(mesh_mutex);
5581 MapSector * ClientMap::emergeSector(v2s16 p2d)
5583 DSTACK(__FUNCTION_NAME);
5584 // Check that it doesn't exist already
5586 return getSectorNoGenerate(p2d);
5588 catch(InvalidPositionException &e)
5593 ClientMapSector *sector = new ClientMapSector(this, p2d);
5596 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5597 m_sectors.insert(p2d, sector);
5603 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
5605 DSTACK(__FUNCTION_NAME);
5606 ClientMapSector *sector = NULL;
5608 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5610 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
5614 sector = (ClientMapSector*)n->getValue();
5615 assert(sector->getId() == MAPSECTOR_CLIENT);
5619 sector = new ClientMapSector(this, p2d);
5621 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5622 m_sectors.insert(p2d, sector);
5626 sector->deSerialize(is);
5629 void ClientMap::OnRegisterSceneNode()
5633 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5634 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5637 ISceneNode::OnRegisterSceneNode();
5640 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5642 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5643 DSTACK(__FUNCTION_NAME);
5645 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5648 Get time for measuring timeout.
5650 Measuring time is very useful for long delays when the
5651 machine is swapping a lot.
5653 int time1 = time(0);
5655 //u32 daynight_ratio = m_client->getDayNightRatio();
5657 m_camera_mutex.Lock();
5658 v3f camera_position = m_camera_position;
5659 v3f camera_direction = m_camera_direction;
5660 m_camera_mutex.Unlock();
5663 Get all blocks and draw all visible ones
5666 v3s16 cam_pos_nodes(
5667 camera_position.X / BS,
5668 camera_position.Y / BS,
5669 camera_position.Z / BS);
5671 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5673 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5674 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5676 // Take a fair amount as we will be dropping more out later
5678 p_nodes_min.X / MAP_BLOCKSIZE - 1,
5679 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
5680 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
5682 p_nodes_max.X / MAP_BLOCKSIZE + 1,
5683 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
5684 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
5686 u32 vertex_count = 0;
5688 // For limiting number of mesh updates per frame
5689 u32 mesh_update_count = 0;
5691 u32 blocks_would_have_drawn = 0;
5692 u32 blocks_drawn = 0;
5694 //NOTE: The sectors map should be locked but we're not doing it
5695 // because it'd cause too much delays
5697 int timecheck_counter = 0;
5698 core::map<v2s16, MapSector*>::Iterator si;
5699 si = m_sectors.getIterator();
5700 for(; si.atEnd() == false; si++)
5703 timecheck_counter++;
5704 if(timecheck_counter > 50)
5706 timecheck_counter = 0;
5707 int time2 = time(0);
5708 if(time2 > time1 + 4)
5710 dstream<<"ClientMap::renderMap(): "
5711 "Rendering takes ages, returning."
5718 MapSector *sector = si.getNode()->getValue();
5719 v2s16 sp = sector->getPos();
5721 if(m_control.range_all == false)
5723 if(sp.X < p_blocks_min.X
5724 || sp.X > p_blocks_max.X
5725 || sp.Y < p_blocks_min.Z
5726 || sp.Y > p_blocks_max.Z)
5730 core::list< MapBlock * > sectorblocks;
5731 sector->getBlocks(sectorblocks);
5737 core::list< MapBlock * >::Iterator i;
5738 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5740 MapBlock *block = *i;
5743 Compare block position to camera position, skip
5744 if not seen on display
5747 float range = 100000 * BS;
5748 if(m_control.range_all == false)
5749 range = m_control.wanted_range * BS;
5752 if(isBlockInSight(block->getPos(), camera_position,
5753 camera_direction, range, &d) == false)
5758 // This is ugly (spherical distance limit?)
5759 /*if(m_control.range_all == false &&
5760 d - 0.5*BS*MAP_BLOCKSIZE > range)
5765 Update expired mesh (used for day/night change)
5767 It doesn't work exactly like it should now with the
5768 tasked mesh update but whatever.
5771 bool mesh_expired = false;
5774 JMutexAutoLock lock(block->mesh_mutex);
5776 mesh_expired = block->getMeshExpired();
5778 // Mesh has not been expired and there is no mesh:
5779 // block has no content
5780 if(block->mesh == NULL && mesh_expired == false)
5784 f32 faraway = BS*50;
5785 //f32 faraway = m_control.wanted_range * BS;
5788 This has to be done with the mesh_mutex unlocked
5790 // Pretty random but this should work somewhat nicely
5791 if(mesh_expired && (
5792 (mesh_update_count < 3
5793 && (d < faraway || mesh_update_count < 2)
5796 (m_control.range_all && mesh_update_count < 20)
5799 /*if(mesh_expired && mesh_update_count < 6
5800 && (d < faraway || mesh_update_count < 3))*/
5802 mesh_update_count++;
5804 // Mesh has been expired: generate new mesh
5805 //block->updateMesh(daynight_ratio);
5806 m_client->addUpdateMeshTask(block->getPos());
5808 mesh_expired = false;
5813 Draw the faces of the block
5816 JMutexAutoLock lock(block->mesh_mutex);
5818 scene::SMesh *mesh = block->mesh;
5823 blocks_would_have_drawn++;
5824 if(blocks_drawn >= m_control.wanted_max_blocks
5825 && m_control.range_all == false
5826 && d > m_control.wanted_min_range * BS)
5830 u32 c = mesh->getMeshBufferCount();
5832 for(u32 i=0; i<c; i++)
5834 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
5835 const video::SMaterial& material = buf->getMaterial();
5836 video::IMaterialRenderer* rnd =
5837 driver->getMaterialRenderer(material.MaterialType);
5838 bool transparent = (rnd && rnd->isTransparent());
5839 // Render transparent on transparent pass and likewise.
5840 if(transparent == is_transparent_pass)
5843 This *shouldn't* hurt too much because Irrlicht
5844 doesn't change opengl textures if the old
5845 material is set again.
5847 driver->setMaterial(buf->getMaterial());
5848 driver->drawMeshBuffer(buf);
5849 vertex_count += buf->getVertexCount();
5853 } // foreach sectorblocks
5856 m_control.blocks_drawn = blocks_drawn;
5857 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
5859 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
5860 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
5863 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
5864 core::map<v3s16, MapBlock*> *affected_blocks)
5866 bool changed = false;
5868 Add it to all blocks touching it
5871 v3s16(0,0,0), // this
5872 v3s16(0,0,1), // back
5873 v3s16(0,1,0), // top
5874 v3s16(1,0,0), // right
5875 v3s16(0,0,-1), // front
5876 v3s16(0,-1,0), // bottom
5877 v3s16(-1,0,0), // left
5879 for(u16 i=0; i<7; i++)
5881 v3s16 p2 = p + dirs[i];
5882 // Block position of neighbor (or requested) node
5883 v3s16 blockpos = getNodeBlockPos(p2);
5884 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5885 if(blockref == NULL)
5887 // Relative position of requested node
5888 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5889 if(blockref->setTempMod(relpos, mod))
5894 if(changed && affected_blocks!=NULL)
5896 for(u16 i=0; i<7; i++)
5898 v3s16 p2 = p + dirs[i];
5899 // Block position of neighbor (or requested) node
5900 v3s16 blockpos = getNodeBlockPos(p2);
5901 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5902 if(blockref == NULL)
5904 affected_blocks->insert(blockpos, blockref);
5910 bool ClientMap::clearTempMod(v3s16 p,
5911 core::map<v3s16, MapBlock*> *affected_blocks)
5913 bool changed = false;
5915 v3s16(0,0,0), // this
5916 v3s16(0,0,1), // back
5917 v3s16(0,1,0), // top
5918 v3s16(1,0,0), // right
5919 v3s16(0,0,-1), // front
5920 v3s16(0,-1,0), // bottom
5921 v3s16(-1,0,0), // left
5923 for(u16 i=0; i<7; i++)
5925 v3s16 p2 = p + dirs[i];
5926 // Block position of neighbor (or requested) node
5927 v3s16 blockpos = getNodeBlockPos(p2);
5928 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5929 if(blockref == NULL)
5931 // Relative position of requested node
5932 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5933 if(blockref->clearTempMod(relpos))
5938 if(changed && affected_blocks!=NULL)
5940 for(u16 i=0; i<7; i++)
5942 v3s16 p2 = p + dirs[i];
5943 // Block position of neighbor (or requested) node
5944 v3s16 blockpos = getNodeBlockPos(p2);
5945 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5946 if(blockref == NULL)
5948 affected_blocks->insert(blockpos, blockref);
5954 void ClientMap::expireMeshes(bool only_daynight_diffed)
5956 TimeTaker timer("expireMeshes()");
5958 core::map<v2s16, MapSector*>::Iterator si;
5959 si = m_sectors.getIterator();
5960 for(; si.atEnd() == false; si++)
5962 MapSector *sector = si.getNode()->getValue();
5964 core::list< MapBlock * > sectorblocks;
5965 sector->getBlocks(sectorblocks);
5967 core::list< MapBlock * >::Iterator i;
5968 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5970 MapBlock *block = *i;
5972 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
5978 JMutexAutoLock lock(block->mesh_mutex);
5979 if(block->mesh != NULL)
5981 /*block->mesh->drop();
5982 block->mesh = NULL;*/
5983 block->setMeshExpired(true);
5990 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
5992 assert(mapType() == MAPTYPE_CLIENT);
5995 v3s16 p = blockpos + v3s16(0,0,0);
5996 MapBlock *b = getBlockNoCreate(p);
5997 b->updateMesh(daynight_ratio);
5998 //b->setMeshExpired(true);
6000 catch(InvalidPositionException &e){}
6003 v3s16 p = blockpos + v3s16(-1,0,0);
6004 MapBlock *b = getBlockNoCreate(p);
6005 b->updateMesh(daynight_ratio);
6006 //b->setMeshExpired(true);
6008 catch(InvalidPositionException &e){}
6010 v3s16 p = blockpos + v3s16(0,-1,0);
6011 MapBlock *b = getBlockNoCreate(p);
6012 b->updateMesh(daynight_ratio);
6013 //b->setMeshExpired(true);
6015 catch(InvalidPositionException &e){}
6017 v3s16 p = blockpos + v3s16(0,0,-1);
6018 MapBlock *b = getBlockNoCreate(p);
6019 b->updateMesh(daynight_ratio);
6020 //b->setMeshExpired(true);
6022 catch(InvalidPositionException &e){}
6027 Update mesh of block in which the node is, and if the node is at the
6028 leading edge, update the appropriate leading blocks too.
6030 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
6038 v3s16 blockposes[4];
6039 for(u32 i=0; i<4; i++)
6041 v3s16 np = nodepos + dirs[i];
6042 blockposes[i] = getNodeBlockPos(np);
6043 // Don't update mesh of block if it has been done already
6044 bool already_updated = false;
6045 for(u32 j=0; j<i; j++)
6047 if(blockposes[j] == blockposes[i])
6049 already_updated = true;
6056 MapBlock *b = getBlockNoCreate(blockposes[i]);
6057 b->updateMesh(daynight_ratio);
6062 void ClientMap::PrintInfo(std::ostream &out)
6073 MapVoxelManipulator::MapVoxelManipulator(Map *map)
6078 MapVoxelManipulator::~MapVoxelManipulator()
6080 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
6084 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
6086 TimeTaker timer1("emerge", &emerge_time);
6088 // Units of these are MapBlocks
6089 v3s16 p_min = getNodeBlockPos(a.MinEdge);
6090 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
6092 VoxelArea block_area_nodes
6093 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6095 addArea(block_area_nodes);
6097 for(s32 z=p_min.Z; z<=p_max.Z; z++)
6098 for(s32 y=p_min.Y; y<=p_max.Y; y++)
6099 for(s32 x=p_min.X; x<=p_max.X; x++)
6102 core::map<v3s16, bool>::Node *n;
6103 n = m_loaded_blocks.find(p);
6107 bool block_data_inexistent = false;
6110 TimeTaker timer1("emerge load", &emerge_load_time);
6112 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
6113 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
6116 dstream<<std::endl;*/
6118 MapBlock *block = m_map->getBlockNoCreate(p);
6119 if(block->isDummy())
6120 block_data_inexistent = true;
6122 block->copyTo(*this);
6124 catch(InvalidPositionException &e)
6126 block_data_inexistent = true;
6129 if(block_data_inexistent)
6131 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6132 // Fill with VOXELFLAG_INEXISTENT
6133 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
6134 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
6136 s32 i = m_area.index(a.MinEdge.X,y,z);
6137 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
6141 m_loaded_blocks.insert(p, !block_data_inexistent);
6144 //dstream<<"emerge done"<<std::endl;
6148 SUGG: Add an option to only update eg. water and air nodes.
6149 This will make it interfere less with important stuff if
6152 void MapVoxelManipulator::blitBack
6153 (core::map<v3s16, MapBlock*> & modified_blocks)
6155 if(m_area.getExtent() == v3s16(0,0,0))
6158 //TimeTaker timer1("blitBack");
6160 /*dstream<<"blitBack(): m_loaded_blocks.size()="
6161 <<m_loaded_blocks.size()<<std::endl;*/
6164 Initialize block cache
6166 v3s16 blockpos_last;
6167 MapBlock *block = NULL;
6168 bool block_checked_in_modified = false;
6170 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
6171 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
6172 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
6176 u8 f = m_flags[m_area.index(p)];
6177 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
6180 MapNode &n = m_data[m_area.index(p)];
6182 v3s16 blockpos = getNodeBlockPos(p);
6187 if(block == NULL || blockpos != blockpos_last){
6188 block = m_map->getBlockNoCreate(blockpos);
6189 blockpos_last = blockpos;
6190 block_checked_in_modified = false;
6193 // Calculate relative position in block
6194 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
6196 // Don't continue if nothing has changed here
6197 if(block->getNode(relpos) == n)
6200 //m_map->setNode(m_area.MinEdge + p, n);
6201 block->setNode(relpos, n);
6204 Make sure block is in modified_blocks
6206 if(block_checked_in_modified == false)
6208 modified_blocks[blockpos] = block;
6209 block_checked_in_modified = true;
6212 catch(InvalidPositionException &e)
6218 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
6219 MapVoxelManipulator(map),
6220 m_create_area(false)
6224 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
6228 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
6230 // Just create the area so that it can be pointed to
6231 VoxelManipulator::emerge(a, caller_id);
6234 void ManualMapVoxelManipulator::initialEmerge(
6235 v3s16 blockpos_min, v3s16 blockpos_max)
6237 TimeTaker timer1("initialEmerge", &emerge_time);
6239 // Units of these are MapBlocks
6240 v3s16 p_min = blockpos_min;
6241 v3s16 p_max = blockpos_max;
6243 VoxelArea block_area_nodes
6244 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6246 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
6249 dstream<<"initialEmerge: area: ";
6250 block_area_nodes.print(dstream);
6251 dstream<<" ("<<size_MB<<"MB)";
6255 addArea(block_area_nodes);
6257 for(s32 z=p_min.Z; z<=p_max.Z; z++)
6258 for(s32 y=p_min.Y; y<=p_max.Y; y++)
6259 for(s32 x=p_min.X; x<=p_max.X; x++)
6262 core::map<v3s16, bool>::Node *n;
6263 n = m_loaded_blocks.find(p);
6267 bool block_data_inexistent = false;
6270 TimeTaker timer1("emerge load", &emerge_load_time);
6272 MapBlock *block = m_map->getBlockNoCreate(p);
6273 if(block->isDummy())
6274 block_data_inexistent = true;
6276 block->copyTo(*this);
6278 catch(InvalidPositionException &e)
6280 block_data_inexistent = true;
6283 if(block_data_inexistent)
6286 Mark area inexistent
6288 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6289 // Fill with VOXELFLAG_INEXISTENT
6290 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
6291 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
6293 s32 i = m_area.index(a.MinEdge.X,y,z);
6294 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
6298 m_loaded_blocks.insert(p, !block_data_inexistent);
6302 void ManualMapVoxelManipulator::blitBackAll(
6303 core::map<v3s16, MapBlock*> * modified_blocks)
6305 if(m_area.getExtent() == v3s16(0,0,0))
6309 Copy data of all blocks
6311 for(core::map<v3s16, bool>::Iterator
6312 i = m_loaded_blocks.getIterator();
6313 i.atEnd() == false; i++)
6315 bool existed = i.getNode()->getValue();
6316 if(existed == false)
6318 v3s16 p = i.getNode()->getKey();
6319 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
6322 dstream<<"WARNING: "<<__FUNCTION_NAME
6323 <<": got NULL block "
6324 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
6329 block->copyFrom(*this);
6332 modified_blocks->insert(p, block);