TerraForged/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/TerraChunkGenerator.java

367 lines
15 KiB
Java

/*
*
* MIT License
*
* Copyright (c) 2020 TerraForged
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.terraforged.mod.chunk;
import com.terraforged.api.chunk.column.ColumnDecorator;
import com.terraforged.api.chunk.column.DecoratorContext;
import com.terraforged.api.chunk.surface.ChunkSurfaceBuffer;
import com.terraforged.api.chunk.surface.SurfaceContext;
import com.terraforged.api.chunk.surface.SurfaceManager;
import com.terraforged.api.material.layer.LayerManager;
import com.terraforged.core.cell.Cell;
import com.terraforged.core.region.RegionCache;
import com.terraforged.core.region.RegionGenerator;
import com.terraforged.core.region.Size;
import com.terraforged.core.region.chunk.ChunkReader;
import com.terraforged.core.util.concurrent.ThreadPool;
import com.terraforged.feature.FeatureManager;
import com.terraforged.feature.matcher.dynamic.DynamicMatcher;
import com.terraforged.feature.matcher.feature.FeatureMatcher;
import com.terraforged.feature.modifier.FeatureModifierLoader;
import com.terraforged.feature.modifier.FeatureModifiers;
import com.terraforged.feature.predicate.DeepWater;
import com.terraforged.feature.predicate.FeaturePredicate;
import com.terraforged.feature.predicate.MinHeight;
import com.terraforged.feature.template.type.FeatureTypes;
import com.terraforged.mod.Log;
import com.terraforged.mod.biome.provider.BiomeProvider;
import com.terraforged.mod.biome.provider.TerraBiomeManager;
import com.terraforged.mod.chunk.fix.ChunkCarverFix;
import com.terraforged.mod.chunk.fix.RegionFix;
import com.terraforged.mod.decorator.ChunkPopulator;
import com.terraforged.mod.decorator.base.BedrockDecorator;
import com.terraforged.mod.decorator.base.CoastDecorator;
import com.terraforged.mod.decorator.base.ErosionDecorator;
import com.terraforged.mod.decorator.base.GeologyDecorator;
import com.terraforged.mod.decorator.feature.LayerDecorator;
import com.terraforged.mod.decorator.feature.SnowEroder;
import com.terraforged.mod.decorator.surface.FrozenOcean;
import com.terraforged.mod.feature.Matchers;
import com.terraforged.mod.feature.TerrainHelper;
import com.terraforged.mod.feature.predicate.TreeLine;
import com.terraforged.mod.material.Materials;
import com.terraforged.mod.material.geology.GeoManager;
import com.terraforged.mod.util.setup.SetupHooks;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.IWorld;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.BiomeContainer;
import net.minecraft.world.biome.BiomeManager;
import net.minecraft.world.biome.Biomes;
import net.minecraft.world.chunk.ChunkPrimer;
import net.minecraft.world.chunk.IChunk;
import net.minecraft.world.gen.ChunkGenerator;
import net.minecraft.world.gen.GenerationSettings;
import net.minecraft.world.gen.GenerationStage;
import net.minecraft.world.gen.Heightmap;
import net.minecraft.world.gen.WorldGenRegion;
import net.minecraft.world.gen.feature.AbstractTreeFeature;
import net.minecraft.world.gen.feature.Feature;
import net.minecraft.world.gen.feature.template.TemplateManager;
import java.util.ArrayList;
import java.util.List;
public class TerraChunkGenerator extends ObfHelperChunkGenerator<GenerationSettings> {
private final TerraContext context;
private final BiomeProvider biomeProvider;
private final TerrainHelper terrainHelper;
private final BiomeManager biomeManager;
private final GeoManager geologyManager;
private final FeatureManager featureManager;
private final SurfaceManager surfaceManager;
private final List<ColumnDecorator> baseDecorators;
private final List<ColumnDecorator> postProcessors;
private final RegionCache regionCache;
public TerraChunkGenerator(TerraContext context, BiomeProvider biomeProvider, GenerationSettings settings) {
super(context.world, biomeProvider, settings);
this.context = context;
this.biomeProvider = biomeProvider;
this.surfaceManager = SetupHooks.setup(createSurfaceManager(), context.copy());
this.geologyManager = SetupHooks.setup(createGeologyManager(context), context.copy());
this.baseDecorators = createBaseDecorators(context);
this.postProcessors = createFeatureDecorators(context);
this.terrainHelper = new TerrainHelper((int) world.getSeed(), 0.8F);
this.biomeManager = new TerraBiomeManager(biomeProvider, context.world.getSeed());
this.featureManager = createFeatureManager(context);
this.regionCache = createRegionCache(context);
SetupHooks.setup(getLayerManager(), context.copy());
SetupHooks.setup(baseDecorators, postProcessors, context.copy());
}
@Override
public void generateStructures(BiomeManager unused, IChunk chunk, ChunkGenerator<?> generator, TemplateManager templates) {
super.generateStructures(biomeManager, chunk, this, templates);
}
@Override
public final void generateBiomes(IChunk chunk) {
ChunkPos pos = chunk.getPos();
ChunkReader view = getChunkReader(pos.x, pos.z);
BiomeContainer container = getBiomeProvider().createBiomeContainer(view);
((ChunkPrimer) chunk).func_225548_a_(container);
}
@Override
public final void generateBase(IWorld world, IChunk chunk) {
DecoratorContext context = new DecoratorContext(chunk, getContext().levels, getContext().terrain, getContext().factory.getClimate());
TerraContainer container = getBiomeContainer(chunk);
container.getChunkReader().iterate((cell, dx, dz) -> {
int px = context.blockX + dx;
int pz = context.blockZ + dz;
int py = (int) (cell.value * getMaxHeight());
context.cell = cell;
context.biome = container.getBiome(dx, dz);
ChunkPopulator.INSTANCE.decorate(chunk, context, px, py, pz);
});
terrainHelper.flatten(world, chunk, context.blockX, context.blockZ);
}
@Override
public final void generateSurface(WorldGenRegion world, IChunk chunk) {
ChunkSurfaceBuffer buffer = new ChunkSurfaceBuffer(chunk);
SurfaceContext context = getContext().surface(buffer, getSettings());
TerraContainer container = getBiomeContainer(chunk);
container.getChunkReader().iterate((cell, dx, dz) -> {
int px = context.blockX + dx;
int pz = context.blockZ + dz;
int top = chunk.getTopBlockY(Heightmap.Type.WORLD_SURFACE_WG, dx, dz) + 1;
buffer.setSurfaceLevel(top);
context.cell = cell;
context.biome = container.getBiome(dx, dz);
context.noise = getSurfaceNoise(px, pz) * 15D;
getSurfaceManager().getSurface(context).buildSurface(px, pz, top, context);
int py = (int) (cell.value * getMaxHeight());
for (ColumnDecorator processor : getBaseDecorators()) {
processor.decorate(buffer, context, px, py, pz);
}
});
}
@Override
public final void func_225550_a_(BiomeManager biomeManager, IChunk chunk, GenerationStage.Carving carving) {
// World carvers have hardcoded 'carvable' blocks which can be problematic with modded blocks
// AirCarverFix shims the actual blockstates to an equivalent carvable type
super.func_225550_a_(this.biomeManager, new ChunkCarverFix(chunk, context.materials), carving);
}
@Override
public void decorate(WorldGenRegion region) {
int chunkX = region.getMainChunkX();
int chunkZ = region.getMainChunkZ();
IChunk chunk = region.getChunk(chunkX, chunkZ);
TerraContainer container = getBiomeContainer(chunk);
Biome biome = container.getFeatureBiome();
DecoratorContext context = getContext().decorator(chunk);
// overrides WorldGenRegion's getSeaLevel() to provide the actual sea level
IWorld regionFix = new RegionFix(region, container, this, biomeManager);
BlockPos pos = new BlockPos(context.blockX, 0, context.blockZ);
// place biome features
featureManager.decorate(this, regionFix, chunk, biome, pos);
// run post processors
container.getChunkReader().iterate((cell, dx, dz) -> {
int px = context.blockX + dx;
int pz = context.blockZ + dz;
int py = chunk.getTopBlockY(Heightmap.Type.WORLD_SURFACE_WG, dx, dz);
context.cell = cell;
context.biome = container.getBiome(dx, dz);
for (ColumnDecorator decorator : getPostProcessors()) {
decorator.decorate(chunk, context, px, py, pz);
}
});
((ChunkPrimer) chunk).func_225548_a_(container.bakeBiomes());
}
@Override
public int getTopBlockY(int x, int z, Heightmap.Type type) {
int chunkX = Size.blockToChunk(x);
int chunkZ = Size.blockToChunk(z);
ChunkReader chunk = getChunkReader(chunkX, chunkZ);
Cell<?> cell = chunk.getCell(x, z);
return (int) (cell.value * getMaxHeight());
}
@Override
public BiomeProvider getBiomeProvider() {
return biomeProvider;
}
@Override
public final int getMaxHeight() {
return getContext().levels.worldHeight;
}
@Override
public final int getSeaLevel() {
return getContext().levels.waterLevel;
}
@Override
public final int getGroundHeight() {
return getContext().levels.groundLevel;
}
public final TerraContext getContext() {
return context;
}
public final Materials getMaterials() {
return context.materials;
}
public final GeoManager getGeologyManager() {
return geologyManager;
}
public final LayerManager getLayerManager() {
return context.materials.getLayerManager();
}
public final SurfaceManager getSurfaceManager() {
return surfaceManager;
}
public final List<ColumnDecorator> getBaseDecorators() {
return baseDecorators;
}
public final List<ColumnDecorator> getPostProcessors() {
return postProcessors;
}
protected TerraContainer getBiomeContainer(IChunk chunk) {
if (chunk.getBiomes() instanceof TerraContainer) {
return (TerraContainer) chunk.getBiomes();
}
ChunkReader view = getChunkReader(chunk.getPos().x, chunk.getPos().z);
TerraContainer container = getBiomeProvider().createBiomeContainer(view);
if (chunk instanceof ChunkPrimer) {
((ChunkPrimer) chunk).func_225548_a_(container);
}
return container;
}
protected FeatureManager createFeatureManager(TerraContext context) {
FeatureModifiers modifiers;
if (context.terraSettings.features.customBiomeFeatures) {
Log.info(" - Custom biome features enabled");
modifiers = FeatureModifierLoader.load();
} else {
modifiers = new FeatureModifiers();
}
// block ugly features
modifiers.getPredicates().add(Matchers.STONE_BLOBS, FeaturePredicate.DENY);
modifiers.getPredicates().add(FeatureMatcher.of(Feature.DISK), FeaturePredicate.DENY);
modifiers.getPredicates().add(FeatureMatcher.of(Feature.LAKE), FeaturePredicate.DENY);
modifiers.getPredicates().add(FeatureMatcher.of(Feature.SPRING_FEATURE), FeaturePredicate.DENY);
// limit to deep oceans
modifiers.getPredicates().add(FeatureMatcher.of(Feature.SHIPWRECK), DeepWater.INSTANCE);
modifiers.getPredicates().add(FeatureMatcher.of(Feature.OCEAN_RUIN), DeepWater.INSTANCE);
modifiers.getPredicates().add(FeatureMatcher.of(Feature.OCEAN_MONUMENT), DeepWater.INSTANCE);
// prevent mineshafts above ground
modifiers.getPredicates().add(FeatureMatcher.of(Feature.MINESHAFT), MinHeight.HEIGHT80);
// prevent trees/bamboo growing too high up
TreeLine treeLine = new TreeLine(context);
modifiers.getPredicates().add(FeatureTypes.TREE.matcher(), treeLine);
modifiers.getPredicates().add(FeatureMatcher.of(Feature.BAMBOO), treeLine);
modifiers.getDynamic().add(DynamicMatcher.feature(AbstractTreeFeature.class), treeLine);
return FeatureManager.create(context.world, SetupHooks.setup(modifiers, context.copy()));
}
protected GeoManager createGeologyManager(TerraContext context) {
return new GeoManager(context);
}
protected SurfaceManager createSurfaceManager() {
SurfaceManager manager = new SurfaceManager();
manager.replace(Biomes.FROZEN_OCEAN, new FrozenOcean(context, 20, 15));
manager.replace(Biomes.DEEP_FROZEN_OCEAN, new FrozenOcean(context, 30, 30));
return manager;
}
protected List<ColumnDecorator> createBaseDecorators(TerraContext context) {
List<ColumnDecorator> processors = new ArrayList<>();
if (context.terraSettings.features.strataDecorator) {
Log.info(" - Geology decorator enabled");
processors.add(new GeologyDecorator(geologyManager));
}
if (context.terraSettings.features.erosionDecorator) {
Log.info(" - Erosion decorator enabled");
processors.add(new ErosionDecorator(context));
}
processors.add(new CoastDecorator(context));
processors.add(new BedrockDecorator());
return processors;
}
protected List<ColumnDecorator> createFeatureDecorators(TerraContext context) {
List<ColumnDecorator> processors = new ArrayList<>();
if (context.terraSettings.features.naturalSnowDecorator) {
Log.info(" - Natural snow decorator enabled");
processors.add(new SnowEroder(context));
}
if (context.terraSettings.features.smoothLayerDecorator) {
Log.info(" - Smooth layer decorator enabled");
processors.add(new LayerDecorator(context.materials.getLayerManager()));
}
return processors;
}
protected RegionCache createRegionCache(TerraContext context) {
return RegionGenerator.builder()
.pool(ThreadPool.getFixed())
.factory(context.factory)
.size(3, 2)
.build()
.toCache();
}
public ChunkReader getChunkReader(int chunkX, int chunkZ) {
return regionCache.getChunk(chunkX, chunkZ);
}
}