All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cn.nukkit.level.generator.Normal Maven / Gradle / Ivy

There is a newer version: 1.20.40-r1
Show newest version
package cn.nukkit.level.generator;

import cn.nukkit.api.Since;
import cn.nukkit.block.Block;
import cn.nukkit.block.BlockID;
import cn.nukkit.block.BlockStone;
import cn.nukkit.blockstate.BlockState;
import cn.nukkit.level.ChunkManager;
import cn.nukkit.level.biome.Biome;
import cn.nukkit.level.biome.BiomeSelector;
import cn.nukkit.level.biome.EnumBiome;
import cn.nukkit.level.format.generic.BaseFullChunk;
import cn.nukkit.level.generator.noise.vanilla.f.NoiseGeneratorOctavesF;
import cn.nukkit.level.generator.noise.vanilla.f.NoiseGeneratorPerlinF;
import cn.nukkit.level.generator.object.ore.OreType;
import cn.nukkit.level.generator.populator.impl.*;
import cn.nukkit.level.generator.populator.type.Populator;
import cn.nukkit.math.MathHelper;
import cn.nukkit.math.NukkitRandom;
import cn.nukkit.math.Vector3;
import com.google.common.collect.ImmutableList;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Random;

/**
 * Nukkit's terrain generator
 * Originally adapted from the PocketMine-MP generator by NycuRO and CreeperFace
 * Mostly rewritten by DaPorkchop_
 * 

* The following classes, and others related to terrain generation are theirs and are intended for NUKKIT USAGE and should not be copied/translated to other server software * such as BukkitPE, ClearSky, Genisys, PocketMine-MP, or others *

* Normal.java * MushroomPopulator.java * DarkOakTreePopulator.java * JungleBigTreePopulator.java * JungleTreePopulaotr.java * SavannaTreePopulator.java * SwampTreePopulator.java * BasicPopulator.java * TreeGenerator.java * HugeTreesGenerator.java * BeachBiome.java * ColdBeachBiome.java * DesertBiome.java * DesertHillsBiome.java * DesertMBiome.java * ExtremeHillsBiome.java * ExtremeHillsEdgeBiome.java * ExtremeHillsMBiome.java * ExtremeHillsPlusBiome.java * ExtremeHillsPlusMBiome.java * StoneBeachBiome.java * FlowerForestBiome.java * ForestBiome.java * ForestHillsBiome.java * IcePlainsBiome.java * IcePlainsSpikesBiome.java * JungleBiome.java * JungleEdgeBiome.java * JungleEdgeMBiome.java * JungleHillsBiome.java * JungleMBiome.java * MesaBiome.java * MesaBryceBiome.java * MesaPlateauBiome.java * MesaPlateauFBiome.java * MesaPlateauFMBiome.java * MesaPlateauMBiome.java * MushroomIslandBiome.java * MushroomIslandShoreBiome.java * DeepOceanBiome.java * FrozenOceanBiome.java * OceanBiome.java * PlainsBiome.java * SunflowerPlainsBiome.java * FrozenRiverBiome.java * RiverBiome.java * RoofedForestBiome.java * RoofedForestMBiome.java * SavannaBiome.java * SavannaMBiome.java * SavannaPlateauBiome.java * SavannaPlateauMBiome.java * SwampBiome.java * SwamplandMBiome.java * ColdTaigaBiome.java * ColdTaigaHillsBiome.java * ColdTaigaMBiome.java * MegaSpruceTaigaBiome.java * MegaTaigaBiome.java * MegaTagaHillsBiome.java * TaigaBiome.java * TaigaHillsBiome.java * TaigaMBiome.java * CoveredBiome.java * GrassyBiome.java * SandyBiome.java * WateryBiome.java * EnumBiomeBiome.java * PopulatorCount.java * PopulatorSurfaceBlock.java * Normal.java * Nether.java * End.java */ public class Normal extends Generator { public static final int seaHeight = 64; private static final float[] biomeWeights = new float[25]; static { for (int i = -2; i <= 2; ++i) { for (int j = -2; j <= 2; ++j) { biomeWeights[i + 2 + (j + 2) * 5] = (float) (10.0F / Math.sqrt((float) (i * i + j * j) + 0.2F)); } } } public NoiseGeneratorOctavesF scaleNoise; public NoiseGeneratorOctavesF depthNoise; private List populators = Collections.emptyList(); private List generationPopulators = Collections.emptyList(); private ChunkManager level; private Random random; private NukkitRandom nukkitRandom; private long localSeed1; private long localSeed2; private BiomeSelector selector; private final ThreadLocal biomes = ThreadLocal.withInitial(() -> new Biome[10 * 10]); private final ThreadLocal depthRegion = ThreadLocal.withInitial(() -> null); private final ThreadLocal mainNoiseRegion = ThreadLocal.withInitial(() -> null); private final ThreadLocal minLimitRegion = ThreadLocal.withInitial(() -> null); private final ThreadLocal maxLimitRegion = ThreadLocal.withInitial(() -> null); private final ThreadLocal heightMap = ThreadLocal.withInitial(() -> new float[825]); private NoiseGeneratorOctavesF minLimitPerlinNoise; private NoiseGeneratorOctavesF maxLimitPerlinNoise; private NoiseGeneratorOctavesF mainPerlinNoise; private NoiseGeneratorPerlinF surfaceNoise; public Normal() { this(Collections.emptyMap()); } public Normal(Map options) { //Nothing here. Just used for future update. } @Override public int getId() { return TYPE_INFINITE; } @Override public ChunkManager getChunkManager() { return this.level; } @Override public String getName() { return "normal"; } @Override public Map getSettings() { return Collections.emptyMap(); } public Biome pickBiome(int x, int z) { return this.selector.pickBiome(x, z); } @Override public void init(ChunkManager level, NukkitRandom random) { this.level = level; this.nukkitRandom = random; this.random = new Random(); this.nukkitRandom.setSeed(this.level.getSeed()); this.localSeed1 = this.random.nextLong(); this.localSeed2 = this.random.nextLong(); this.nukkitRandom.setSeed(this.level.getSeed()); this.selector = new BiomeSelector(this.nukkitRandom); this.minLimitPerlinNoise = new NoiseGeneratorOctavesF(random, 16); this.maxLimitPerlinNoise = new NoiseGeneratorOctavesF(random, 16); this.mainPerlinNoise = new NoiseGeneratorOctavesF(random, 8); this.surfaceNoise = new NoiseGeneratorPerlinF(random, 4); this.scaleNoise = new NoiseGeneratorOctavesF(random, 10); this.depthNoise = new NoiseGeneratorOctavesF(random, 16); //this should run before all other populators so that we don't do things like generate ground cover on bedrock or something this.generationPopulators = ImmutableList.of( new PopulatorBedrock(), new PopulatorGroundCover() ); this.populators = ImmutableList.of( new PopulatorOre(STONE, new OreType[]{ new OreType(Block.get(BlockID.COAL_ORE), 20, 17, 0, 128), new OreType(Block.get(BlockID.IRON_ORE), 20, 9, 0, 64), new OreType(Block.get(BlockID.REDSTONE_ORE), 8, 8, 0, 16), new OreType(Block.get(BlockID.LAPIS_ORE), 1, 7, 0, 16), new OreType(Block.get(BlockID.GOLD_ORE), 2, 9, 0, 32), new OreType(Block.get(BlockID.DIAMOND_ORE), 1, 8, 0, 16), new OreType(Block.get(BlockID.DIRT), 10, 33, 0, 128), new OreType(Block.get(BlockID.GRAVEL), 8, 33, 0, 128), new OreType(Block.get(BlockID.STONE, BlockStone.GRANITE), 10, 33, 0, 80), new OreType(Block.get(BlockID.STONE, BlockStone.DIORITE), 10, 33, 0, 80), new OreType(Block.get(BlockID.STONE, BlockStone.ANDESITE), 10, 33, 0, 80) }), new PopulatorCaves(), //new PopulatorRavines() new PopulatorSpring(BlockState.of(BlockID.WATER), ImmutableList.of(BlockState.of(BlockID.STONE)), 50, 8, 255), new PopulatorSpring(BlockState.of(BlockID.LAVA), ImmutableList.of(BlockState.of(BlockID.STONE)), 20, 16, 255) ); } @Override public void generateChunk(final int chunkX, final int chunkZ) { int baseX = chunkX << 4; int baseZ = chunkZ << 4; this.nukkitRandom.setSeed(chunkX * this.localSeed1 ^ chunkZ * this.localSeed2 ^ this.level.getSeed()); BaseFullChunk chunk = this.level.getChunk(chunkX, chunkZ); //generate base noise values float[] depthRegion = this.depthNoise.generateNoiseOctaves(this.depthRegion.get(), chunkX * 4, chunkZ * 4, 5, 5, 200f, 200f, 0.5f); this.depthRegion.set(depthRegion); float[] mainNoiseRegion = this.mainPerlinNoise.generateNoiseOctaves(this.mainNoiseRegion.get(), chunkX * 4, 0, chunkZ * 4, 5, 33, 5, 684.412f / 60f, 684.412f / 160f, 684.412f / 60f); this.mainNoiseRegion.set(mainNoiseRegion); float[] minLimitRegion = this.minLimitPerlinNoise.generateNoiseOctaves(this.minLimitRegion.get(), chunkX * 4, 0, chunkZ * 4, 5, 33, 5, 684.412f, 684.412f, 684.412f); this.minLimitRegion.set(minLimitRegion); float[] maxLimitRegion = this.maxLimitPerlinNoise.generateNoiseOctaves(this.maxLimitRegion.get(), chunkX * 4, 0, chunkZ * 4, 5, 33, 5, 684.412f, 684.412f, 684.412f); this.maxLimitRegion.set(maxLimitRegion); float[] heightMap = this.heightMap.get(); //generate heightmap and smooth biome heights int horizCounter = 0; int vertCounter = 0; for (int xSeg = 0; xSeg < 5; ++xSeg) { for (int zSeg = 0; zSeg < 5; ++zSeg) { float heightVariationSum = 0.0F; float baseHeightSum = 0.0F; float biomeWeightSum = 0.0F; Biome biome = this.pickBiome(baseX + (xSeg * 4), baseZ + (zSeg * 4)); for (int xSmooth = -2; xSmooth <= 2; ++xSmooth) { for (int zSmooth = -2; zSmooth <= 2; ++zSmooth) { Biome biome1 = this.pickBiome(baseX + (xSeg * 4) + xSmooth, baseZ + (zSeg * 4) + zSmooth); float baseHeight = biome1.getBaseHeight(); float heightVariation = biome1.getHeightVariation(); float scaledWeight = biomeWeights[xSmooth + 2 + (zSmooth + 2) * 5] / (baseHeight + 2.0F); if (biome1.getBaseHeight() > biome.getBaseHeight()) { scaledWeight /= 2.0F; } heightVariationSum += heightVariation * scaledWeight; baseHeightSum += baseHeight * scaledWeight; biomeWeightSum += scaledWeight; } } heightVariationSum = heightVariationSum / biomeWeightSum; baseHeightSum = baseHeightSum / biomeWeightSum; heightVariationSum = heightVariationSum * 0.9F + 0.1F; baseHeightSum = (baseHeightSum * 4.0F - 1.0F) / 8.0F; float depthNoise = depthRegion[vertCounter] / 8000.0f; if (depthNoise < 0.0f) { depthNoise = -depthNoise * 0.3f; } depthNoise = depthNoise * 3.0f - 2.0f; if (depthNoise < 0.0f) { depthNoise = depthNoise / 2.0f; if (depthNoise < -1.0f) { depthNoise = -1.0f; } depthNoise = depthNoise / 1.4f; depthNoise = depthNoise / 2.0f; } else { if (depthNoise > 1.0f) { depthNoise = 1.0f; } depthNoise = depthNoise / 8.0f; } ++vertCounter; float baseHeightClone = baseHeightSum; float heightVariationClone = heightVariationSum; baseHeightClone = baseHeightClone + depthNoise * 0.2f; baseHeightClone = baseHeightClone * 8.5f / 8.0f; float baseHeightFactor = 8.5f + baseHeightClone * 4.0f; for (int ySeg = 0; ySeg < 33; ++ySeg) { float baseScale = ((float) ySeg - baseHeightFactor) * 12f * 128.0f / 256.0f / heightVariationClone; if (baseScale < 0.0f) { baseScale *= 4.0f; } float minScaled = minLimitRegion[horizCounter] / 512f; float maxScaled = maxLimitRegion[horizCounter] / 512f; float noiseScaled = (mainNoiseRegion[horizCounter] / 10.0f + 1.0f) / 2.0f; float clamp = MathHelper.denormalizeClamp(minScaled, maxScaled, noiseScaled) - baseScale; if (ySeg > 29) { float yScaled = ((float) (ySeg - 29) / 3.0F); clamp = clamp * (1.0f - yScaled) + -10.0f * yScaled; } heightMap[horizCounter] = clamp; ++horizCounter; } } } //place blocks for (int xSeg = 0; xSeg < 4; ++xSeg) { int xScale = xSeg * 5; int xScaleEnd = (xSeg + 1) * 5; for (int zSeg = 0; zSeg < 4; ++zSeg) { int zScale1 = (xScale + zSeg) * 33; int zScaleEnd1 = (xScale + zSeg + 1) * 33; int zScale2 = (xScaleEnd + zSeg) * 33; int zScaleEnd2 = (xScaleEnd + zSeg + 1) * 33; for (int ySeg = 0; ySeg < 32; ++ySeg) { double height1 = heightMap[zScale1 + ySeg]; double height2 = heightMap[zScaleEnd1 + ySeg]; double height3 = heightMap[zScale2 + ySeg]; double height4 = heightMap[zScaleEnd2 + ySeg]; double height5 = (heightMap[zScale1 + ySeg + 1] - height1) * 0.125f; double height6 = (heightMap[zScaleEnd1 + ySeg + 1] - height2) * 0.125f; double height7 = (heightMap[zScale2 + ySeg + 1] - height3) * 0.125f; double height8 = (heightMap[zScaleEnd2 + ySeg + 1] - height4) * 0.125f; for (int yIn = 0; yIn < 8; ++yIn) { double baseIncr = height1; double baseIncr2 = height2; double scaleY = (height3 - height1) * 0.25f; double scaleY2 = (height4 - height2) * 0.25f; for (int zIn = 0; zIn < 4; ++zIn) { double scaleZ = (baseIncr2 - baseIncr) * 0.25f; double scaleZ2 = baseIncr - scaleZ; for (int xIn = 0; xIn < 4; ++xIn) { if ((scaleZ2 += scaleZ) > 0.0f) { chunk.setBlockId(xSeg * 4 + zIn, ySeg * 8 + yIn, zSeg * 4 + xIn, STONE); } else if (ySeg * 8 + yIn <= seaHeight) { chunk.setBlockId(xSeg * 4 + zIn, ySeg * 8 + yIn, zSeg * 4 + xIn, STILL_WATER); } } baseIncr += scaleY; baseIncr2 += scaleY2; } height1 += height5; height2 += height6; height3 += height7; height4 += height8; } } } } for (int x = 0; x < 16; x++) { for (int z = 0; z < 16; z++) { Biome biome = this.selector.pickBiome(baseX | x, baseZ | z); chunk.setBiome(x, z, biome); } } //populate chunk for (Populator populator : this.generationPopulators) { populator.populate(this.level, chunkX, chunkZ, this.nukkitRandom, chunk); } } @Override public void populateChunk(int chunkX, int chunkZ) { BaseFullChunk chunk = this.level.getChunk(chunkX, chunkZ); this.nukkitRandom.setSeed(0xdeadbeef ^ ((long) chunkX << 8) ^ chunkZ ^ this.level.getSeed()); for (Populator populator : this.populators) { populator.populate(this.level, chunkX, chunkZ, this.nukkitRandom, chunk); } @SuppressWarnings("deprecation") Biome biome = EnumBiome.getBiome(chunk.getBiomeId(7, 7)); biome.populateChunk(this.level, chunkX, chunkZ, this.nukkitRandom); } @Since("1.19.21-r2") @Override public boolean shouldGenerateStructures() { return true; } @Override public Vector3 getSpawn() { return new Vector3(0.5, 256, 0.5); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy