Skip to content

Instantly share code, notes, and snippets.

@alcatrazEscapee
Created December 7, 2021 15:59
Show Gist options
  • Save alcatrazEscapee/424167705fbab4113ddc931bd1935ba9 to your computer and use it in GitHub Desktop.
Save alcatrazEscapee/424167705fbab4113ddc931bd1935ba9 to your computer and use it in GitHub Desktop.
Custom Ore Veins in 1.18, using Vanilla noise-based ore vein systems. DON'T DO THIS, JUST MAKE A FEATURE!
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.*;
import net.minecraft.world.level.levelgen.synth.NormalNoise;
import org.jetbrains.annotations.Nullable;
/**
* This is an identical copy of the relevant parts of {@link NoiseSampler} and {@link NoiseChunk}
* In practice, this can be whatever you want.
*/
public class CustomOreVeinNoiseSampler
{
private final PositionalRandomFactory oreVeinsPositionalRandomFactory;
private final NoiseChunk.InterpolatableNoise veininess;
private final NoiseChunk.InterpolatableNoise veinA;
private final NoiseChunk.InterpolatableNoise veinB;
private final NormalNoise gapNoise;
private final BlockState ore;
private final BlockState rawOreBlock;
private final BlockState filler;
private final int minY;
private final int maxY;
private static NoiseChunk.InterpolatableNoise yLimitedInterpolatableNoise(NormalNoise noise, int minY, int maxY, double scale)
{
// The createNoiseInterpolator here is needed because of how it interacts with the noise chunk
// The interpolator is stored on the noise chunk (this returns a factory), and then sampled efficiently later
return chunk -> ((NoiseChunkAccessor) chunk).call$createNoiseInterpolator((x, y, z) -> y <= maxY && y >= minY ? noise.getValue(x * scale, y * scale, z * scale) : 0);
}
public CustomOreVeinNoiseSampler(long seed, Registry<NormalNoise.NoiseParameters> parameters)
{
// Initialize the sampler on a per-chunk-generator basis
// sets up any noise or other random parameters
this.ore = Blocks.DIAMOND_ORE.defaultBlockState();
this.rawOreBlock = Blocks.COAL_ORE.defaultBlockState();
this.filler = Blocks.BASALT.defaultBlockState();
this.minY = -64;
this.maxY = 64;
final PositionalRandomFactory fork = new XoroshiroRandomSource(seed).forkPositional();
this.oreVeinsPositionalRandomFactory = fork.fromHashOf(new ResourceLocation("ore")).forkPositional();
this.veininess = yLimitedInterpolatableNoise(Noises.instantiate(parameters, fork, Noises.ORE_VEININESS), minY, maxY, 1.5D);
this.veinA = yLimitedInterpolatableNoise(Noises.instantiate(parameters, fork, Noises.ORE_VEIN_A), minY, maxY, 4.0D);
this.veinB = yLimitedInterpolatableNoise(Noises.instantiate(parameters, fork, Noises.ORE_VEIN_B), minY, maxY, 4.0D);
this.gapNoise = Noises.instantiate(parameters, fork, Noises.ORE_GAP);
}
public class CustomOreVeinNoiseChunk
{
private final NoiseChunk.Sampler veininessSampler;
private final NoiseChunk.Sampler veinASampler;
private final NoiseChunk.Sampler veinBSampler;
public CustomOreVeinNoiseChunk(NoiseChunk chunk)
{
// Initialize the vein on a per-chunk basis
// Calling into NoiseChunk.instantiate means we get these automatically trilinearly interpolated for us
this.veininessSampler = veininess.instantiate(chunk);
this.veinASampler = veinA.instantiate(chunk);
this.veinBSampler = veinB.instantiate(chunk);
}
@Nullable
public BlockState sample(int x, int y, int z)
{
final RandomSource random = oreVeinsPositionalRandomFactory.at(x, y, z);
final double veininess = veininessSampler.sample();
if (isVeinType(veininess, y) && random.nextFloat() < 0.7 && isVein(veinASampler.sample(), veinBSampler.sample()))
{
double chance = Mth.clampedMap(Math.abs(veininess), 0.4, 0.6, 0.1, 0.3);
if (random.nextFloat() < chance && gapNoise.getValue(x, y, z) > -0.3)
{
return random.nextFloat() < 0.02F ? rawOreBlock : ore;
}
else
{
return filler;
}
}
return null;
}
private boolean isVeinType(double veininess, int y)
{
final int belowTop = maxY - y;
final int aboveBottom = y - minY;
if (aboveBottom >= 0 && belowTop >= 0)
{
final int delta = Math.min(belowTop, aboveBottom);
double clampedDelta = Mth.clampedMap(delta, 0, 20, -0.2, 0);
return Math.abs(veininess) + clampedDelta >= 0.4;
}
return false;
}
private boolean isVein(double veinA, double veinB)
{
return Math.max(Math.abs(veinA) - 0.08, Math.abs(veinB) - 0.08) < 0;
}
}
}
import java.util.List;
import net.minecraft.world.level.levelgen.material.MaterialRuleList;
import net.minecraft.world.level.levelgen.material.WorldGenMaterialRule;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.gen.Accessor;
// Both an accessor a mutator are needed because the field itself is an immutable list.
// This could be an AT entry instead though.
@Mixin(MaterialRuleList.class)
public interface MaterialRuleListAccessor
{
@Mutable
@Accessor("materialRuleList")
void setMaterialRuleList(List<WorldGenMaterialRule> materialRuleList);
@Accessor("materialRuleList")
List<WorldGenMaterialRule> getMaterialRuleList();
}
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
import net.minecraft.core.Registry;
import net.minecraft.world.level.biome.BiomeSource;
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
import net.minecraft.world.level.levelgen.NoiseGeneratorSettings;
import net.minecraft.world.level.levelgen.material.WorldGenMaterialRule;
import net.minecraft.world.level.levelgen.synth.NormalNoise;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
// This hooks the creation of noise based chunk generators (all of which have the default ore veins)
// This modifies the rule list, and adds our effective noise sampler / noise chunk combo.
@Mixin(NoiseBasedChunkGenerator.class)
public abstract class NoiseBasedChunkGeneratorMixin
{
@Shadow @Final private WorldGenMaterialRule materialRule;
@Inject(method = "<init>(Lnet/minecraft/core/Registry;Lnet/minecraft/world/level/biome/BiomeSource;Lnet/minecraft/world/level/biome/BiomeSource;JLjava/util/function/Supplier;)V", at = @At("TAIL"))
private void addToMaterialRuleList(Registry<NormalNoise.NoiseParameters> noiseParameters, BiomeSource biomeSource
, BiomeSource runtimeBiomeSource, long seed, Supplier<NoiseGeneratorSettings> settings, CallbackInfo ci)
{
List<WorldGenMaterialRule> list = ((MaterialRuleListAccessor) materialRule).getMaterialRuleList();
list = new ArrayList<>(list);
// This is the money line, and it shows how we use the noise chunk to actually do the sampling.
list.add((chunk, x, y, z) -> ((NoiseChunkBridge) chunk).getCustomOreVeinNoiseChunk().sample(x, y, z));
((MaterialRuleListAccessor) materialRule).setMaterialRuleList(list);
}
}
import net.minecraft.world.level.levelgen.NoiseChunk;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
// This is needed to use NoiseChunk.NoiseInterpolator.
// If you don't need to use vanilla perlerp then what are you doing trying to make noise additions
// Seriously just make a feature
@Mixin(NoiseChunk.class)
public interface NoiseChunkAccessor
{
@Invoker("createNoiseInterpolator")
NoiseChunk.NoiseInterpolator call$createNoiseInterpolator(NoiseChunk.NoiseFiller filler);
}
// Access our custom noise chunk from the vanilla noise chunk
public interface NoiseChunkBridge
{
CustomOreVeinNoiseSampler.CustomOreVeinNoiseChunk getCustomOreVeinNoiseChunk();
}
import net.minecraft.world.level.levelgen.Aquifer;
import net.minecraft.world.level.levelgen.NoiseChunk;
import net.minecraft.world.level.levelgen.NoiseGeneratorSettings;
import net.minecraft.world.level.levelgen.NoiseSampler;
import net.minecraft.world.level.levelgen.blending.Blender;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
// Initializes the custom ore veins noise chunk, and exposes it through NoiseChunkBridge
@Mixin(NoiseChunk.class)
public abstract class NoiseChunkMixin implements NoiseChunkBridge
{
private CustomOreVeinNoiseSampler.CustomOreVeinNoiseChunk customOreVeinNoiseChunk;
@Inject(method = "<init>", at = @At("TAIL"))
private void initializeCustomOreVeinSamplers(int cellCountXZ, int cellCountY, int cellNoiseMinY, NoiseSampler sampler, int blockX, int blockZ, NoiseChunk.NoiseFiller beardifier, NoiseGeneratorSettings settings, Aquifer.FluidPicker globalFluidPicker, Blender oldTerrainBlender, CallbackInfo ci)
{
customOreVeinNoiseChunk = ((NoiseSamplerBridge) sampler).getCustomOreVeinNoiseSampler().new CustomOreVeinNoiseChunk((NoiseChunk) (Object) this);
}
@Override
public CustomOreVeinNoiseSampler.CustomOreVeinNoiseChunk getCustomOreVeinNoiseChunk()
{
return customOreVeinNoiseChunk;
}
}
// Access our custom noise sampler from the vanilla noise sampler
public interface NoiseSamplerBridge
{
CustomOreVeinNoiseSampler getCustomOreVeinNoiseSampler();
}
import net.minecraft.core.Registry;
import net.minecraft.world.level.levelgen.NoiseSampler;
import net.minecraft.world.level.levelgen.NoiseSettings;
import net.minecraft.world.level.levelgen.WorldgenRandom;
import net.minecraft.world.level.levelgen.synth.NormalNoise;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
// Initialize the custom ore veins noise sampler, and exposes it through NoiseSamplerBridge
@Mixin(NoiseSampler.class)
public abstract class NoiseSamplerMixin implements NoiseSamplerBridge
{
private CustomOreVeinNoiseSampler customOreVeinNoiseSampler;
@Inject(method = "<init>", at = @At("TAIL"))
private void setupCustomOreVeinNoiseSamplers(NoiseSettings settings, boolean noiseCavesEnabled, long seed, Registry<NormalNoise.NoiseParameters> parameters, WorldgenRandom.Algorithm algorithm, CallbackInfo ci)
{
customOreVeinNoiseSampler = new CustomOreVeinNoiseSampler(seed, parameters);
}
@Override
public CustomOreVeinNoiseSampler getCustomOreVeinNoiseSampler()
{
return customOreVeinNoiseSampler;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment