implement beardification on Bukkit (closes #370)

This commit is contained in:
dfsek
2022-11-07 17:49:49 -07:00
parent 75a2dd7b6e
commit 1306dad776
4 changed files with 127 additions and 1 deletions

View File

@@ -5,8 +5,10 @@ import ca.solostudios.strata.version.Version;
import com.dfsek.terra.api.addon.BaseAddon;
import com.dfsek.terra.api.event.events.config.ConfigurationLoadEvent;
import com.dfsek.terra.api.event.events.config.pack.ConfigPackPreLoadEvent;
import com.dfsek.terra.api.event.functional.FunctionalEventHandler;
import com.dfsek.terra.api.world.biome.Biome;
import com.dfsek.terra.bukkit.config.PreLoadCompatibilityOptions;
import com.dfsek.terra.bukkit.config.VanillaBiomeProperties;
@@ -21,6 +23,12 @@ public class BukkitAddon implements BaseAddon {
@Override
public void initialize() {
terraBukkitPlugin.getEventManager()
.getHandler(FunctionalEventHandler.class)
.register(this, ConfigPackPreLoadEvent.class)
.then(event -> event.getPack().getContext().put(event.loadTemplate(new PreLoadCompatibilityOptions())))
.global();
terraBukkitPlugin.getEventManager()
.getHandler(FunctionalEventHandler.class)
.register(this, ConfigurationLoadEvent.class)

View File

@@ -0,0 +1,60 @@
/*
* This file is part of Terra.
*
* Terra is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Terra is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Terra. If not, see <https://www.gnu.org/licenses/>.
*/
package com.dfsek.terra.bukkit.config;
import com.dfsek.tectonic.api.config.template.ConfigTemplate;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.terra.api.properties.Properties;
@SuppressWarnings("FieldMayBeFinal")
public class PreLoadCompatibilityOptions implements ConfigTemplate, Properties {
@Value("minecraft.use-vanilla-biomes")
@Default
private boolean vanillaBiomes = false;
@Value("minecraft.beard.enable")
@Default
private boolean beard = true;
@Value("minecraft.beard.threshold")
@Default
private double beardThreshold = 0.5;
@Value("minecraft.beard.air-threshold")
@Default
private double airThreshold = -0.5;
public boolean useVanillaBiomes() {
return vanillaBiomes;
}
public boolean isBeard() {
return beard;
}
public double getBeardThreshold() {
return beardThreshold;
}
public double getAirThreshold() {
return airThreshold;
}
}

View File

@@ -1,5 +1,9 @@
package com.dfsek.terra.bukkit.nms.v1_19_R1;
import com.dfsek.terra.bukkit.config.PreLoadCompatibilityOptions;
import com.dfsek.terra.bukkit.world.BukkitWorldProperties;
import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.Codec;
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
@@ -10,14 +14,21 @@ import net.minecraft.core.SectionPos;
import net.minecraft.server.level.WorldGenRegion;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.LevelHeightAccessor;
import net.minecraft.world.level.NoiseColumn;
import net.minecraft.world.level.StructureManager;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeManager;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.levelgen.Beardifier;
import net.minecraft.world.level.levelgen.DensityFunction;
import net.minecraft.world.level.levelgen.DensityFunction.FunctionContext;
import net.minecraft.world.level.levelgen.DensityFunction.SinglePointContext;
import net.minecraft.world.level.levelgen.GenerationStep.Carving;
import net.minecraft.world.level.levelgen.Heightmap.Types;
import net.minecraft.world.level.levelgen.RandomState;
@@ -103,7 +114,43 @@ public class NMSChunkGeneratorDelegate extends ChunkGenerator {
public @NotNull CompletableFuture<ChunkAccess> fillFromNoise(@NotNull Executor executor, @NotNull Blender blender,
@NotNull RandomState noiseConfig,
@NotNull StructureManager structureAccessor, @NotNull ChunkAccess chunk) {
return vanilla.fillFromNoise(executor, blender, noiseConfig, structureAccessor, chunk);
return vanilla.fillFromNoise(executor, blender, noiseConfig, structureAccessor, chunk)
.thenApply(c -> {
LevelAccessor level = Reflection.STRUCTURE_MANAGER.getLevel(structureAccessor);
BiomeProvider biomeProvider = pack.getBiomeProvider();
PreLoadCompatibilityOptions compatibilityOptions = pack.getContext().get(PreLoadCompatibilityOptions.class);
if(compatibilityOptions.isBeard()) {
beard(structureAccessor, chunk, new BukkitWorldProperties(level.getMinecraftWorld().getWorld()), biomeProvider, compatibilityOptions);
}
return c;
});
}
private void beard(StructureManager structureAccessor, ChunkAccess chunk, WorldProperties world, BiomeProvider biomeProvider,
PreLoadCompatibilityOptions compatibilityOptions) {
Beardifier structureWeightSampler = Beardifier.forStructuresInChunk(structureAccessor, chunk.getPos());
double threshold = compatibilityOptions.getBeardThreshold();
double airThreshold = compatibilityOptions.getAirThreshold();
int xi = chunk.getPos().x << 4;
int zi = chunk.getPos().z << 4;
for(int x = 0; x < 16; x++) {
for(int z = 0; z < 16; z++) {
int depth = 0;
for(int y = world.getMaxHeight(); y >= world.getMinHeight(); y--) {
double noise = structureWeightSampler.compute(new SinglePointContext(x + xi, y, z + zi));
if(noise > threshold) {
chunk.setBlockState(new BlockPos(x, y, z), (BlockState) delegate
.getPalette(x + xi, y, z + zi, world, biomeProvider)
.get(depth, x + xi, y, z + zi, world.getSeed()), false);
depth++;
} else if(noise < airThreshold) {
chunk.setBlockState(new BlockPos(x, y, z), Blocks.AIR.defaultBlockState(), false);
} else {
depth = 0;
}
}
}
}
}
@Override

View File

@@ -1,14 +1,18 @@
package com.dfsek.terra.bukkit.nms.v1_19_R1;
import net.minecraft.core.MappedRegistry;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.StructureManager;
import xyz.jpenilla.reflectionremapper.ReflectionRemapper;
import xyz.jpenilla.reflectionremapper.proxy.ReflectionProxyFactory;
import xyz.jpenilla.reflectionremapper.proxy.annotation.FieldGetter;
import xyz.jpenilla.reflectionremapper.proxy.annotation.FieldSetter;
import xyz.jpenilla.reflectionremapper.proxy.annotation.Proxies;
public class Reflection {
public static final MappedRegistryProxy MAPPED_REGISTRY;
public static final StructureManagerProxy STRUCTURE_MANAGER;
static {
ReflectionRemapper reflectionRemapper = ReflectionRemapper.forReobfMappingsInPaperJar();
@@ -16,6 +20,7 @@ public class Reflection {
Reflection.class.getClassLoader());
MAPPED_REGISTRY = reflectionProxyFactory.reflectionProxy(MappedRegistryProxy.class);
STRUCTURE_MANAGER = reflectionProxyFactory.reflectionProxy(StructureManagerProxy.class);
}
@@ -24,4 +29,10 @@ public class Reflection {
@FieldSetter("frozen")
void setFrozen(MappedRegistry<?> instance, boolean frozen);
}
@Proxies(StructureManager.class)
public interface StructureManagerProxy {
@FieldGetter("level")
LevelAccessor getLevel(StructureManager instance);
}
}