- fix ArrayPool leaking resources to multiple threads

- remove ChunkReader from biome container
- hopefully greatly improved memory efficiency by pooling Cell & Chunk arrays
- added custom snow placer that places snow beneath leaves as well as on top
This commit is contained in:
dags- 2020-06-06 17:39:10 +01:00
parent 94780baf98
commit 282817009e
16 changed files with 230 additions and 109 deletions

View File

@ -31,6 +31,7 @@ import com.terraforged.data.DataGen;
import com.terraforged.feature.context.ContextSelectorFeature;
import com.terraforged.feature.decorator.poisson.PoissonAtSurface;
import com.terraforged.feature.feature.DiskFeature;
import com.terraforged.feature.feature.FreezeLayer;
import com.terraforged.fm.template.TemplateManager;
import com.terraforged.settings.SettingsHelper;
import com.terraforged.util.DataPackFinder;
@ -83,6 +84,7 @@ public class TerraForgedMod {
Log.info("Registering features");
TemplateManager.register(event);
event.getRegistry().register(DiskFeature.INSTANCE);
event.getRegistry().register(FreezeLayer.INSTANCE);
event.getRegistry().register(ContextSelectorFeature.INSTANCE);
}

View File

@ -50,6 +50,7 @@ public class BiomeModifierManager implements BiomeModifier, ModifierManager {
modifiers.add(new BeachModifier(biomes, context));
modifiers.add(new DesertColorModifier(desertBiomes));
modifiers.add(new SandBiomeModifier(context));
modifiers.add(new MushroomModifier());
Collections.sort(modifiers);
this.biomeModifiers = modifiers;
}

View File

@ -0,0 +1,23 @@
package com.terraforged.biome.modifier;
import com.terraforged.api.biome.modifier.BiomeModifier;
import com.terraforged.core.cell.Cell;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.Biomes;
public class MushroomModifier implements BiomeModifier {
@Override
public int priority() {
return 0;
}
@Override
public boolean test(Biome biome) {
return biome == Biomes.MUSHROOM_FIELD_SHORE;
}
@Override
public Biome modify(Biome in, Cell cell, int x, int z) {
return Biomes.MUSHROOM_FIELDS;
}
}

View File

@ -176,7 +176,7 @@ public class TerraChunkGenerator extends ChunkGenerator<GenerationSettings> {
public final int func_222529_a(int x, int z, Heightmap.Type type) {
int chunkX = Size.blockToChunk(x);
int chunkZ = Size.blockToChunk(z);
ChunkReader chunk = getChunkReader(chunkX, chunkZ);
try (ChunkReader chunk = getChunkReader(chunkX, chunkZ)) {
Cell cell = chunk.getCell(x, z);
int level = context.levels.scale(cell.value) + 1;
if (type == Heightmap.Type.OCEAN_FLOOR || type == Heightmap.Type.OCEAN_FLOOR_WG) {
@ -184,6 +184,7 @@ public class TerraChunkGenerator extends ChunkGenerator<GenerationSettings> {
}
return Math.max(getSeaLevel(), level);
}
}
@Override
public BiomeProvider getBiomeProvider() {
@ -260,4 +261,20 @@ public class TerraChunkGenerator extends ChunkGenerator<GenerationSettings> {
public final ChunkReader getChunkReader(int chunkX, int chunkZ) {
return regionCache.getChunk(chunkX, chunkZ);
}
public static ChunkReader getChunk(IWorld world, ChunkGenerator<?> generator) {
if (generator instanceof TerraChunkGenerator) {
TerraChunkGenerator terra = (TerraChunkGenerator) generator;
if (world instanceof IChunk) {
IChunk chunk = (IChunk) world;
return terra.getChunkReader(chunk.getPos().x, chunk.getPos().z);
}
if (world instanceof WorldGenRegion) {
WorldGenRegion region = (WorldGenRegion) world;
return terra.getChunkReader(region.getMainChunkX(), region.getMainChunkZ());
}
}
return null;
}
}

View File

@ -15,6 +15,7 @@ import com.terraforged.chunk.column.post.LayerDecorator;
import com.terraforged.chunk.column.post.SnowEroder;
import com.terraforged.feature.BlockDataManager;
import com.terraforged.feature.Matchers;
import com.terraforged.feature.feature.FreezeLayer;
import com.terraforged.fm.FeatureManager;
import com.terraforged.fm.data.DataManager;
import com.terraforged.fm.matcher.biome.BiomeMatcher;
@ -24,6 +25,7 @@ import com.terraforged.fm.predicate.DeepWater;
import com.terraforged.fm.predicate.FeaturePredicate;
import com.terraforged.fm.predicate.MinHeight;
import com.terraforged.fm.structure.StructureManager;
import com.terraforged.fm.transformer.FeatureTransformer;
import com.terraforged.material.geology.GeoManager;
import com.terraforged.util.setup.SetupHooks;
import net.minecraft.world.biome.Biome;
@ -90,6 +92,13 @@ public class TerraSetupFactory {
if (context.terraSettings.features.customBiomeFeatures) {
// remove default trees from river biomes since forests can go up to the edge of rivers
modifiers.getPredicates().add(BiomeMatcher.of(Biome.Category.RIVER), Matchers.tree(), FeaturePredicate.DENY);
// places snow layers below and on top of trees
modifiers.getTransformers().add(
BiomeMatcher.ANY,
FeatureMatcher.of(Feature.FREEZE_TOP_LAYER),
FeatureTransformer.replace(Feature.FREEZE_TOP_LAYER, FreezeLayer.INSTANCE)
);
}
// block ugly features

View File

@ -1,7 +1,9 @@
package com.terraforged.chunk.generator;
import com.terraforged.biome.provider.BiomeProvider;
import com.terraforged.chunk.TerraChunkGenerator;
import com.terraforged.chunk.util.TerraContainer;
import com.terraforged.core.region.chunk.ChunkReader;
import com.terraforged.world.terrain.decorator.Decorator;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.biome.Biome;
@ -10,23 +12,27 @@ import net.minecraft.world.chunk.IChunk;
public class BiomeGenerator {
private final TerraChunkGenerator generator;
private final BiomeProvider biomeProvider;
public BiomeGenerator(TerraChunkGenerator generator) {
this.generator = generator;
this.biomeProvider = generator.getBiomeProvider();
}
public void generateBiomes(IChunk chunk) {
ChunkPos pos = chunk.getPos();
TerraContainer container = TerraContainer.getOrCreate(chunk, generator);
try (ChunkReader reader = generator.getChunkReader(pos.x, pos.z)) {
TerraContainer container = TerraContainer.create(reader, generator.getBiomeProvider());
// apply chunk-local heightmap modifications
preProcess(pos, container);
preProcess(reader, container);
}
}
private void preProcess(ChunkPos pos, TerraContainer container) {
container.getChunkReader().iterate((cell, dx, dz) -> {
Biome biome = container.getBiome(dx, dz);
private void preProcess(ChunkReader reader, TerraContainer biomes) {
reader.iterate((cell, dx, dz) -> {
Biome biome = biomes.getBiome(dx, dz);
for (Decorator decorator : generator.getBiomeProvider().getDecorators(biome)) {
if (decorator.apply(cell, pos.getXStart() + dx, pos.getZStart() + dz)) {
if (decorator.apply(cell, reader.getBlockX() + dx, reader.getBlockZ() + dz)) {
return;
}
}

View File

@ -29,10 +29,11 @@ public class FeatureGenerator {
int chunkX = region.getMainChunkX();
int chunkZ = region.getMainChunkZ();
IChunk chunk = region.getChunk(chunkX, chunkZ);
TerraContainer container = TerraContainer.getOrCreate(chunk, generator);
ChunkReader chunkReader = generator.getChunkReader(chunkX, chunkZ);
Biome biome = container.getFeatureBiome(chunkReader);
ChunkReader reader = generator.getChunkReader(chunkX, chunkZ);
TerraContainer container = TerraContainer.getOrCreate(chunk, reader, generator.getBiomeProvider());
Biome biome = container.getFeatureBiome(reader);
DecoratorContext context = generator.getContext().decorator(chunk);
IWorld regionFix = new RegionFix(region, generator);
@ -42,15 +43,21 @@ public class FeatureGenerator {
generator.getFeatureManager().decorate(generator, regionFix, chunk, biome, pos);
// run post processes on chunk
postProcess(chunkReader, container, context);
postProcess(reader, container, context);
// bake biome array & discard gen data
// bake biome array
((ChunkPrimer) chunk).func_225548_a_(container.bakeBiomes(Environment.isVanillaBiomes()));
// close the current chunk reader
reader.close();
// mark chunk disposed as this is the last usage of the reader
reader.dispose();
}
private void postProcess(ChunkReader chunk, TerraContainer container, DecoratorContext context) {
private void postProcess(ChunkReader reader, TerraContainer container, DecoratorContext context) {
List<ColumnDecorator> decorators = generator.getPostProcessors();
container.getChunkReader().iterate(context, (cell, dx, dz, ctx) -> {
reader.iterate(context, (cell, dx, dz, ctx) -> {
int px = ctx.blockX + dx;
int pz = ctx.blockZ + dz;
int py = ctx.chunk.getTopBlockY(Heightmap.Type.WORLD_SURFACE_WG, dx, dz);

View File

@ -6,6 +6,7 @@ import com.terraforged.api.chunk.surface.SurfaceContext;
import com.terraforged.chunk.TerraChunkGenerator;
import com.terraforged.chunk.util.FastChunk;
import com.terraforged.chunk.util.TerraContainer;
import com.terraforged.core.region.chunk.ChunkReader;
import net.minecraft.util.SharedSeedRandom;
import net.minecraft.world.chunk.IChunk;
import net.minecraft.world.gen.Heightmap;
@ -24,11 +25,12 @@ public class SurfaceGenerator {
}
public final void generateSurface(WorldGenRegion world, IChunk chunk) {
TerraContainer container = TerraContainer.getOrCreate(chunk, generator);
try (ChunkReader reader = generator.getChunkReader(chunk.getPos().x, chunk.getPos().z)) {
TerraContainer container = TerraContainer.getOrCreate(chunk, reader, generator.getBiomeProvider());
ChunkSurfaceBuffer buffer = new ChunkSurfaceBuffer(FastChunk.wrap(chunk));
try (SurfaceContext context = generator.getContext().surface(buffer, generator.getSettings())) {
container.getChunkReader().iterate(context, (cell, dx, dz, ctx) -> {
reader.iterate(context, (cell, dx, dz, ctx) -> {
int px = ctx.blockX + dx;
int pz = ctx.blockZ + dz;
int top = ctx.chunk.getTopBlockY(Heightmap.Type.WORLD_SURFACE_WG, dx, dz);
@ -50,6 +52,7 @@ public class SurfaceGenerator {
FastChunk.updateWGHeightmaps(chunk, context.pos);
}
}
}
private double getSurfaceNoise(int x, int z) {
double scale = 0.0625D;

View File

@ -5,6 +5,7 @@ import com.terraforged.chunk.TerraChunkGenerator;
import com.terraforged.chunk.column.ChunkPopulator;
import com.terraforged.chunk.util.FastChunk;
import com.terraforged.chunk.util.TerraContainer;
import com.terraforged.core.region.chunk.ChunkReader;
import com.terraforged.feature.TerrainHelper;
import com.terraforged.world.climate.Climate;
import com.terraforged.world.heightmap.Levels;
@ -29,9 +30,10 @@ public class TerrainGenerator {
}
public final void generateTerrain(IWorld world, IChunk chunk) {
TerraContainer container = TerraContainer.getOrCreate(chunk, generator);
try (ChunkReader reader = generator.getChunkReader(chunk.getPos().x, chunk.getPos().z)) {
TerraContainer container = TerraContainer.getOrCreate(chunk, reader, generator.getBiomeProvider());
try (DecoratorContext context = new DecoratorContext(FastChunk.wrap(chunk), levels, terrain, climate)) {
container.getChunkReader().iterate(context, (cell, dx, dz, ctx) -> {
reader.iterate(context, (cell, dx, dz, ctx) -> {
int px = ctx.blockX + dx;
int pz = ctx.blockZ + dz;
int py = ctx.levels.scale(cell.value);
@ -43,3 +45,4 @@ public class TerrainGenerator {
}
}
}
}

View File

@ -1,11 +1,10 @@
package com.terraforged.chunk.util;
import com.terraforged.api.biome.BiomeVariant;
import com.terraforged.chunk.TerraChunkGenerator;
import com.terraforged.biome.provider.BiomeProvider;
import com.terraforged.core.cell.Cell;
import com.terraforged.core.region.chunk.ChunkReader;
import com.terraforged.core.util.PosIterator;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.BiomeContainer;
@ -23,17 +22,11 @@ public class TerraContainer extends BiomeContainer {
private final Biome[] biomes;
private final Biome[] surface;
private final ChunkReader chunkReader;
public TerraContainer(Biome[] biomes, Biome[] surface, ChunkReader chunkReader) {
public TerraContainer(Biome[] biomes, Biome[] surface) {
super(biomes);
this.biomes = biomes;
this.surface = surface;
this.chunkReader = chunkReader;
}
public ChunkReader getChunkReader() {
return chunkReader;
}
public Biome getBiome(int x, int z) {
@ -68,28 +61,26 @@ public class TerraContainer extends BiomeContainer {
return new BiomeContainer(biomes);
}
public static TerraContainer getOrCreate(IChunk chunk, TerraChunkGenerator generator) {
public static TerraContainer getOrCreate(IChunk chunk, ChunkReader reader, BiomeProvider biomeProvider) {
if (chunk.getBiomes() instanceof TerraContainer) {
return (TerraContainer) chunk.getBiomes();
} else {
TerraContainer container = TerraContainer.create(generator, chunk.getPos());
TerraContainer container = TerraContainer.create(reader, biomeProvider);
((ChunkPrimer) chunk).func_225548_a_(container);
return container;
}
}
public static TerraContainer create(TerraChunkGenerator generator, ChunkPos pos) {
ChunkReader reader = generator.getChunkReader(pos.x, pos.z);
public static TerraContainer create(ChunkReader chunkReader, BiomeProvider biomeProvider) {
Biome[] biomes2D = new Biome[BIOMES_2D_SIZE];
Biome[] biomes3D = new Biome[BIOMES_3D_SIZE];
PosIterator iterator = PosIterator.area(0, 0, 16, 16);
while (iterator.next()) {
int dx = iterator.x();
int dz = iterator.z();
int x = pos.getXStart() + dx;
int z = pos.getZStart() + dz;
Biome biome = generator.getBiomeProvider().getBiome(reader.getCell(dx, dz), x, z);
int x = chunkReader.getBlockX() + dx;
int z = chunkReader.getBlockZ() + dz;
Biome biome = biomeProvider.getBiome(chunkReader.getCell(dx, dz), x, z);
biomes2D[indexOf(dx, dz)] = biome;
if ((dx & 3) == 0 && (dz & 3) == 0) {
for (int dy = 0; dy < 64; dy++) {
@ -97,8 +88,7 @@ public class TerraContainer extends BiomeContainer {
}
}
}
return new TerraContainer(biomes3D, biomes2D, reader);
return new TerraContainer(biomes3D, biomes2D);
}
private static int indexOf(int x, int z) {

View File

@ -1,21 +1,22 @@
package com.terraforged.feature.context;
import com.terraforged.chunk.TerraChunkGenerator;
import com.terraforged.chunk.fix.RegionDelegate;
import com.terraforged.chunk.util.TerraContainer;
import com.terraforged.core.cell.Cell;
import com.terraforged.core.concurrent.Resource;
import com.terraforged.core.concurrent.cache.SafeCloseable;
import com.terraforged.core.concurrent.pool.ObjectPool;
import com.terraforged.core.region.chunk.ChunkReader;
import com.terraforged.world.heightmap.Levels;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IWorld;
import net.minecraft.world.biome.BiomeContainer;
import net.minecraft.world.chunk.IChunk;
import net.minecraft.world.gen.ChunkGenerator;
import net.minecraft.world.gen.WorldGenRegion;
import java.util.Random;
public class ChanceContext {
public class ChanceContext implements SafeCloseable {
private static final ObjectPool<ChanceContext> pool = new ObjectPool<>(10, ChanceContext::new);
@ -28,6 +29,14 @@ public class ChanceContext {
private float total = 0F;
private float[] buffer;
@Override
public void close() {
if (reader != null) {
reader.close();
reader = null;
}
}
void setPos(BlockPos pos) {
cell = reader.getCell(pos.getX(), pos.getZ());
}
@ -61,25 +70,16 @@ public class ChanceContext {
return -1;
}
public static Resource<ChanceContext> pooled(IWorld world) {
if (world instanceof RegionDelegate) {
Levels levels = new Levels(world.getMaxHeight(), world.getSeaLevel());
public static Resource<ChanceContext> pooled(IWorld world, ChunkGenerator<?> generator) {
if (generator instanceof TerraChunkGenerator && world instanceof RegionDelegate) {
TerraChunkGenerator terraGenerator = (TerraChunkGenerator) generator;
Levels levels = terraGenerator.getContext().levels;
WorldGenRegion region = ((RegionDelegate) world).getRegion();
IChunk chunk = region.getChunk(region.getMainChunkX(), region.getMainChunkZ());
return pooled(chunk, levels);
}
return null;
}
public static Resource<ChanceContext> pooled(IChunk chunk, Levels levels) {
BiomeContainer container = chunk.getBiomes();
if (container instanceof TerraContainer) {
ChunkReader reader = ((TerraContainer) container).getChunkReader();
Resource<ChanceContext> item = pool.get();
ChanceContext context = item.get();
context.chunk = chunk;
context.levels = levels;
context.reader = reader;
item.get().chunk = chunk;
item.get().levels = levels;
item.get().reader = terraGenerator.getChunkReader(chunk.getPos().x, chunk.getPos().z);
return item;
}
return null;

View File

@ -20,7 +20,7 @@ public class ContextSelectorFeature extends Feature<ContextSelectorConfig> {
@Override
public boolean place(IWorld world, ChunkGenerator<? extends GenerationSettings> generator, Random random, BlockPos pos, ContextSelectorConfig config) {
try (Resource<ChanceContext> item = ChanceContext.pooled(world)) {
try (Resource<ChanceContext> item = ChanceContext.pooled(world, generator)) {
if (item == null) {
return false;
}

View File

@ -1,16 +1,13 @@
package com.terraforged.feature.decorator.poisson;
import com.terraforged.chunk.fix.RegionDelegate;
import com.terraforged.chunk.util.TerraContainer;
import com.terraforged.chunk.TerraChunkGenerator;
import com.terraforged.core.cell.Cell;
import com.terraforged.core.region.chunk.ChunkReader;
import me.dags.noise.Module;
import me.dags.noise.Source;
import me.dags.noise.util.NoiseUtil;
import net.minecraft.world.IWorld;
import net.minecraft.world.biome.BiomeContainer;
import net.minecraft.world.chunk.IChunk;
import net.minecraft.world.gen.WorldGenRegion;
import net.minecraft.world.gen.ChunkGenerator;
public class BiomeVariance implements Module {
@ -32,19 +29,9 @@ public class BiomeVariance implements Module {
return NoiseUtil.map(1 - cell.biomeEdge, MIN_FADE, fade, range);
}
public static Module of(IWorld world, float fade) {
if (world instanceof RegionDelegate) {
WorldGenRegion region = ((RegionDelegate) world).getRegion();
IChunk chunk = region.getChunk(region.getMainChunkX(), region.getMainChunkZ());
return of(chunk, fade);
}
return Source.ONE;
}
public static Module of(IChunk chunk, float fade) {
BiomeContainer container = chunk.getBiomes();
if (container instanceof TerraContainer) {
ChunkReader reader = ((TerraContainer) container).getChunkReader();
public static Module of(IWorld world, ChunkGenerator<?> generator, float fade) {
ChunkReader reader = TerraChunkGenerator.getChunk(world, generator);
if (reader != null) {
return new BiomeVariance(reader, fade);
}
return Source.ONE;

View File

@ -7,6 +7,7 @@ import com.terraforged.core.util.poisson.PoissonContext;
import me.dags.noise.Module;
import me.dags.noise.Source;
import net.minecraft.world.IWorld;
import net.minecraft.world.gen.ChunkGenerator;
import net.minecraft.world.gen.placement.IPlacementConfig;
public class PoissonConfig implements IPlacementConfig {
@ -25,12 +26,12 @@ public class PoissonConfig implements IPlacementConfig {
this.densityNoiseMax = densityNoiseMax;
}
public void apply(IWorld world, PoissonContext context) {
public void apply(IWorld world, ChunkGenerator<?> generator, PoissonContext context) {
Module fade = Source.ONE;
Module density = Source.ONE;
if (biomeFade > 0.075F) {
fade = BiomeVariance.of(world, biomeFade);
fade = BiomeVariance.of(world, generator, biomeFade);
}
if (densityNoiseScale > 0) {

View File

@ -27,7 +27,7 @@ public abstract class PoissonDecorator extends Placement<PoissonConfig> {
int radius = Math.max(1, Math.min(30, config.radius));
Poisson poisson = getInstance(radius);
PoissonVisitor visitor = new PoissonVisitor(this, feature, world, generator, random, pos);
config.apply(world, visitor);
config.apply(world, generator, visitor);
int chunkX = pos.getX() >> 4;
int chunkZ = pos.getZ() >> 4;
poisson.visit(chunkX, chunkZ, visitor, visitor);

View File

@ -0,0 +1,72 @@
package com.terraforged.feature.feature;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.block.SnowyDirtBlock;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IWorld;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.gen.ChunkGenerator;
import net.minecraft.world.gen.GenerationSettings;
import net.minecraft.world.gen.Heightmap;
import net.minecraft.world.gen.feature.Feature;
import net.minecraft.world.gen.feature.NoFeatureConfig;
import java.util.Random;
public class FreezeLayer extends Feature<NoFeatureConfig> {
public static final FreezeLayer INSTANCE = new FreezeLayer();
public FreezeLayer() {
super(NoFeatureConfig::deserialize);
setRegistryName("terraforged", "freeze_top_layer");
}
@Override
public boolean place(IWorld world, ChunkGenerator<? extends GenerationSettings> generator, Random rand, BlockPos pos, NoFeatureConfig config) {
BlockPos.Mutable pos1 = new BlockPos.Mutable();
BlockPos.Mutable pos2 = new BlockPos.Mutable();
for(int dx = 0; dx < 16; ++dx) {
for(int dz = 0; dz < 16; ++dz) {
int x = pos.getX() + dx;
int z = pos.getZ() + dz;
int y1 = world.getHeight(Heightmap.Type.MOTION_BLOCKING, x, z);
int y2 = world.getHeight(Heightmap.Type.MOTION_BLOCKING_NO_LEAVES, x, z);
pos1.setPos(x, y1, z);
pos2.setPos(pos1).move(Direction.DOWN, 1);
Biome biome = world.getBiome(pos1);
boolean freezesHere = freeze(world, biome, pos1, pos2, false);
if (y1 != y2) {
pos1.setPos(x, y2, z);
pos2.setPos(pos1).move(Direction.DOWN, 1);
freeze(world, biome, pos1, pos2, freezesHere);
}
}
}
return true;
}
private boolean freeze(IWorld world, Biome biome, BlockPos top, BlockPos below, boolean force) {
boolean hasFrozen = false;
if (biome.doesWaterFreeze(world, below, false)) {
world.setBlockState(below, Blocks.ICE.getDefaultState(), 2);
hasFrozen = true;
}
if (force || biome.doesSnowGenerate(world, top)) {
hasFrozen = true;
world.setBlockState(top, Blocks.SNOW.getDefaultState(), 2);
BlockState blockstate = world.getBlockState(below);
if (blockstate.has(SnowyDirtBlock.SNOWY)) {
world.setBlockState(below, blockstate.with(SnowyDirtBlock.SNOWY, true), 2);
}
}
return hasFrozen;
}
}