46 private int $waterHeight = 62;
48 private array $populators = [];
50 private array $generationPopulators = [];
59 parent::__construct($seed, $preset);
63 $this->noiseBase =
new Simplex($this->random, 4, 1 / 4, 1 / 32);
64 $this->random->setSeed($this->seed);
66 $this->selector =
new class($this->random) extends
BiomeSelector{
67 protected function lookup(
float $temperature,
float $rainfall) :
int{
69 if($temperature < 0.7){
70 return BiomeIds::OCEAN;
71 }elseif($temperature < 0.85){
72 return BiomeIds::RIVER;
74 return BiomeIds::SWAMPLAND;
76 }elseif($rainfall < 0.60){
77 if($temperature < 0.25){
78 return BiomeIds::ICE_PLAINS;
79 }elseif($temperature < 0.75){
80 return BiomeIds::PLAINS;
82 return BiomeIds::DESERT;
84 }elseif($rainfall < 0.80){
85 if($temperature < 0.25){
86 return BiomeIds::TAIGA;
87 }elseif($temperature < 0.75){
88 return BiomeIds::FOREST;
90 return BiomeIds::BIRCH_FOREST;
93 if($temperature < 0.20){
94 return BiomeIds::EXTREME_HILLS;
95 }elseif($temperature < 0.40){
96 return BiomeIds::EXTREME_HILLS_EDGE;
98 return BiomeIds::RIVER;
104 $this->selector->recalculate();
107 $this->generationPopulators[] = $cover;
110 $stone = VanillaBlocks::STONE();
112 new OreType(VanillaBlocks::COAL_ORE(), $stone, 20, 16, 0, 128),
113 new OreType(VanillaBlocks::IRON_ORE(), $stone, 20, 8, 0, 64),
114 new OreType(VanillaBlocks::REDSTONE_ORE(), $stone, 8, 7, 0, 16),
115 new OreType(VanillaBlocks::LAPIS_LAZULI_ORE(), $stone, 1, 6, 0, 32),
116 new OreType(VanillaBlocks::GOLD_ORE(), $stone, 2, 8, 0, 32),
117 new OreType(VanillaBlocks::DIAMOND_ORE(), $stone, 1, 7, 0, 16),
118 new OreType(VanillaBlocks::DIRT(), $stone, 20, 32, 0, 128),
119 new OreType(VanillaBlocks::GRAVEL(), $stone, 10, 16, 0, 128)
121 $this->populators[] = $ores;
124 private function pickBiome(
int $x,
int $z) :
Biome{
125 $hash = $x * 2345803 ^ $z * 9236449 ^ $this->seed;
126 $hash *= $hash + 223;
130 $hash = (int) fmod($hash, 2.0 ** 63);
131 $xNoise = $hash >> 20 & 3;
132 $zNoise = $hash >> 22 & 3;
140 return $this->selector->pickBiome($x + $xNoise - 1, $z + $zNoise - 1);
143 public function generateChunk(ChunkManager $world,
int $chunkX,
int $chunkZ) : void{
144 $this->random->setSeed(0xdeadbeef ^ ($chunkX << 8) ^ $chunkZ ^ $this->seed);
146 $noise = $this->noiseBase->getFastNoise3D(Chunk::EDGE_LENGTH, 128, Chunk::EDGE_LENGTH, 4, 8, 4, $chunkX * Chunk::EDGE_LENGTH, 0, $chunkZ * Chunk::EDGE_LENGTH);
149 $chunk = $world->getChunk($chunkX, $chunkZ) ??
throw new \InvalidArgumentException(
"Chunk $chunkX $chunkZ does not yet exist");
153 $bedrock = VanillaBlocks::BEDROCK()->getStateId();
154 $stillWater = VanillaBlocks::WATER()->getStateId();
155 $stone = VanillaBlocks::STONE()->getStateId();
157 $baseX = $chunkX * Chunk::EDGE_LENGTH;
158 $baseZ = $chunkZ * Chunk::EDGE_LENGTH;
159 for($x = 0; $x < Chunk::EDGE_LENGTH; ++$x){
160 $absoluteX = $baseX + $x;
161 for($z = 0; $z < Chunk::EDGE_LENGTH; ++$z){
162 $absoluteZ = $baseZ + $z;
167 $biome = $this->pickBiome($absoluteX, $absoluteZ);
168 for($y = World::Y_MIN; $y < World::Y_MAX; $y++){
169 $chunk->setBiomeId($x, $y, $z, $biome->getId());
172 for($sx = -$this->gaussian->smoothSize; $sx <= $this->gaussian->smoothSize; ++$sx){
173 for($sz = -$this->gaussian->smoothSize; $sz <= $this->gaussian->smoothSize; ++$sz){
175 $weight = $this->gaussian->kernel[$sx + $this->gaussian->smoothSize][$sz + $this->gaussian->smoothSize];
177 if($sx === 0 && $sz === 0){
181 if(isset($biomeCache[$index])){
182 $adjacent = $biomeCache[$index];
184 $biomeCache[$index] = $adjacent = $this->pickBiome($absoluteX + $sx, $absoluteZ + $sz);
188 $minSum += ($adjacent->getMinElevation() - 1) * $weight;
189 $maxSum += $adjacent->getMaxElevation() * $weight;
191 $weightSum += $weight;
195 $minSum /= $weightSum;
196 $maxSum /= $weightSum;
198 $smoothHeight = ($maxSum - $minSum) / 2;
200 for($y = 0; $y < 128; ++$y){
202 $chunk->setBlockStateId($x, $y, $z, $bedrock);
205 $noiseValue = $noise[$x][$z][$y] - 1 / $smoothHeight * ($y - $smoothHeight - $minSum);
208 $chunk->setBlockStateId($x, $y, $z, $stone);
209 }elseif($y <= $this->waterHeight){
210 $chunk->setBlockStateId($x, $y, $z, $stillWater);
216 foreach($this->generationPopulators as $populator){
217 $populator->populate($world, $chunkX, $chunkZ, $this->random);
221 public function populateChunk(ChunkManager $world,
int $chunkX,
int $chunkZ) : void{
222 $this->random->setSeed(0xdeadbeef ^ ($chunkX << 8) ^ $chunkZ ^ $this->seed);
223 foreach($this->populators as $populator){
224 $populator->populate($world, $chunkX, $chunkZ, $this->random);
227 $chunk = $world->getChunk($chunkX, $chunkZ);
228 $biome = BiomeRegistry::getInstance()->getBiome($chunk->getBiomeId(7, 7, 7));
229 $biome->populateChunk($world, $chunkX, $chunkZ, $this->random);