mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-07-24 07:40:56 +00:00
JIGGY!
This commit is contained in:
+69
-14
@@ -25,28 +25,29 @@ import org.bukkit.craftbukkit.v1_21_R7.CraftWorld;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class CustomBiomeSource extends BiomeSource {
|
||||
private static final int NOISE_BIOME_CACHE_MAX = 262144;
|
||||
|
||||
private final long seed;
|
||||
private final Engine engine;
|
||||
private final Registry<Biome> biomeCustomRegistry;
|
||||
private final Registry<Biome> biomeRegistry;
|
||||
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
||||
private final RNG rng;
|
||||
private final KMap<String, Holder<Biome>> customBiomes;
|
||||
private final Holder<Biome> fallbackBiome;
|
||||
private final ConcurrentHashMap<Long, Holder<Biome>> noiseBiomeCache = new ConcurrentHashMap<>();
|
||||
|
||||
public CustomBiomeSource(long seed, Engine engine, World world) {
|
||||
this.engine = engine;
|
||||
this.seed = seed;
|
||||
this.biomeCustomRegistry = registry().lookup(Registries.BIOME).orElse(null);
|
||||
this.biomeRegistry = ((RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer())).lookup(Registries.BIOME).orElse(null);
|
||||
this.rng = new RNG(engine.getSeedManager().getBiome());
|
||||
this.fallbackBiome = resolveFallbackBiome(this.biomeRegistry, this.biomeCustomRegistry);
|
||||
this.customBiomes = fillCustomBiomes(this.biomeCustomRegistry, engine, this.fallbackBiome);
|
||||
}
|
||||
@@ -172,31 +173,85 @@ public class CustomBiomeSource extends BiomeSource {
|
||||
|
||||
@Override
|
||||
public Holder<Biome> getNoiseBiome(int x, int y, int z, Climate.Sampler sampler) {
|
||||
int m = (y - engine.getMinHeight()) << 2;
|
||||
IrisBiome ib = engine.getComplex().getTrueBiomeStream().get(x << 2, z << 2);
|
||||
if (ib == null) {
|
||||
return resolveFallbackBiome(biomeRegistry, biomeCustomRegistry);
|
||||
long cacheKey = packNoiseKey(x, y, z);
|
||||
Holder<Biome> cachedHolder = noiseBiomeCache.get(cacheKey);
|
||||
if (cachedHolder != null) {
|
||||
return cachedHolder;
|
||||
}
|
||||
|
||||
if (ib.isCustom()) {
|
||||
IrisBiomeCustom custom = ib.getCustomBiome(rng, x << 2, m, z << 2);
|
||||
if (custom != null) {
|
||||
Holder<Biome> holder = customBiomes.get(custom.getId());
|
||||
Holder<Biome> resolvedHolder = resolveNoiseBiomeHolder(x, y, z);
|
||||
Holder<Biome> existingHolder = noiseBiomeCache.putIfAbsent(cacheKey, resolvedHolder);
|
||||
if (existingHolder != null) {
|
||||
return existingHolder;
|
||||
}
|
||||
|
||||
if (noiseBiomeCache.size() > NOISE_BIOME_CACHE_MAX) {
|
||||
noiseBiomeCache.clear();
|
||||
}
|
||||
|
||||
return resolvedHolder;
|
||||
}
|
||||
|
||||
private Holder<Biome> resolveNoiseBiomeHolder(int x, int y, int z) {
|
||||
if (engine == null || engine.isClosed()) {
|
||||
return getFallbackBiome();
|
||||
}
|
||||
|
||||
if (engine.getComplex() == null) {
|
||||
return getFallbackBiome();
|
||||
}
|
||||
|
||||
int blockX = x << 2;
|
||||
int blockZ = z << 2;
|
||||
int blockY = (y - engine.getMinHeight()) << 2;
|
||||
IrisBiome irisBiome = engine.getComplex().getTrueBiomeStream().get(blockX, blockZ);
|
||||
if (irisBiome == null) {
|
||||
return getFallbackBiome();
|
||||
}
|
||||
|
||||
RNG noiseRng = new RNG(seed
|
||||
^ (((long) blockX) * 341873128712L)
|
||||
^ (((long) blockY) * 132897987541L)
|
||||
^ (((long) blockZ) * 42317861L));
|
||||
|
||||
if (irisBiome.isCustom()) {
|
||||
IrisBiomeCustom customBiome = irisBiome.getCustomBiome(noiseRng, blockX, blockY, blockZ);
|
||||
if (customBiome != null) {
|
||||
Holder<Biome> holder = customBiomes.get(customBiome.getId());
|
||||
if (holder != null) {
|
||||
return holder;
|
||||
}
|
||||
}
|
||||
|
||||
return resolveFallbackBiome(biomeRegistry, biomeCustomRegistry);
|
||||
return getFallbackBiome();
|
||||
}
|
||||
|
||||
org.bukkit.block.Biome v = ib.getSkyBiome(rng, x << 2, m, z << 2);
|
||||
Holder<Biome> holder = NMSBinding.biomeToBiomeBase(biomeRegistry, v);
|
||||
org.bukkit.block.Biome vanillaBiome = irisBiome.getSkyBiome(noiseRng, blockX, blockY, blockZ);
|
||||
Holder<Biome> holder = NMSBinding.biomeToBiomeBase(biomeRegistry, vanillaBiome);
|
||||
if (holder != null) {
|
||||
return holder;
|
||||
}
|
||||
|
||||
return resolveFallbackBiome(biomeRegistry, biomeCustomRegistry);
|
||||
return getFallbackBiome();
|
||||
}
|
||||
|
||||
private Holder<Biome> getFallbackBiome() {
|
||||
if (fallbackBiome != null) {
|
||||
return fallbackBiome;
|
||||
}
|
||||
|
||||
Holder<Biome> holder = resolveFallbackBiome(biomeRegistry, biomeCustomRegistry);
|
||||
if (holder != null) {
|
||||
return holder;
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Unable to resolve any biome holder fallback for Iris biome source");
|
||||
}
|
||||
|
||||
private static long packNoiseKey(int x, int y, int z) {
|
||||
return (((long) x & 67108863L) << 38)
|
||||
| (((long) z & 67108863L) << 12)
|
||||
| ((long) y & 4095L);
|
||||
}
|
||||
|
||||
private static Holder<Biome> resolveCustomBiomeHolder(Registry<Biome> customRegistry, Engine engine, String customBiomeId) {
|
||||
|
||||
+71
-17
@@ -30,6 +30,7 @@ import net.minecraft.world.level.levelgen.*;
|
||||
import net.minecraft.world.level.levelgen.blending.Blender;
|
||||
import net.minecraft.world.level.levelgen.structure.BoundingBox;
|
||||
import net.minecraft.world.level.levelgen.structure.Structure;
|
||||
import net.minecraft.world.level.levelgen.structure.StructureStart;
|
||||
import net.minecraft.world.level.levelgen.structure.StructureSet;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||
import org.bukkit.Bukkit;
|
||||
@@ -50,6 +51,8 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
private static final WrappedReturningMethod<Heightmap, Object> SET_HEIGHT;
|
||||
private final ChunkGenerator delegate;
|
||||
private final Engine engine;
|
||||
private volatile Registry<Structure> cachedStructureRegistry;
|
||||
private volatile Map<Structure, Integer> cachedStructureOrder;
|
||||
|
||||
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
|
||||
super(((CraftWorld) world).getHandle(), edit(delegate, new CustomBiomeSource(seed, engine, world)), null);
|
||||
@@ -152,20 +155,21 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
public void addVanillaDecorations(WorldGenLevel level, ChunkAccess chunkAccess, StructureManager structureManager) {
|
||||
if (!structureManager.shouldGenerateStructures())
|
||||
return;
|
||||
if (!IrisSettings.get().getGeneral().isAutoGenerateIntrinsicStructures()) {
|
||||
return;
|
||||
}
|
||||
|
||||
SectionPos sectionPos = SectionPos.of(chunkAccess.getPos(), level.getMinSectionY());
|
||||
BlockPos blockPos = sectionPos.origin();
|
||||
WorldgenRandom random = new WorldgenRandom(new XoroshiroRandomSource(RandomSupport.generateUniqueSeed()));
|
||||
long i = random.setDecorationSeed(level.getSeed(), blockPos.getX(), blockPos.getZ());
|
||||
var structures = level.registryAccess().lookupOrThrow(Registries.STRUCTURE);
|
||||
var list = structures.stream()
|
||||
.sorted(Comparator.comparingInt(s -> s.step().ordinal()))
|
||||
.toList();
|
||||
Registry<Structure> structureRegistry = level.registryAccess().lookupOrThrow(Registries.STRUCTURE);
|
||||
Map<Structure, Integer> structureOrder = getStructureOrder(structureRegistry);
|
||||
|
||||
var surface = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.WORLD_SURFACE_WG);
|
||||
var ocean = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.OCEAN_FLOOR_WG);
|
||||
var motion = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.MOTION_BLOCKING);
|
||||
var motionNoLeaves = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES);
|
||||
Heightmap surface = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.WORLD_SURFACE_WG);
|
||||
Heightmap ocean = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.OCEAN_FLOOR_WG);
|
||||
Heightmap motion = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.MOTION_BLOCKING);
|
||||
Heightmap motionNoLeaves = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES);
|
||||
|
||||
for (int x = 0; x < 16; x++) {
|
||||
for (int z = 0; z < 16; z++) {
|
||||
@@ -174,21 +178,42 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
|
||||
int noAir = engine.getHeight(wX, wZ, false) + engine.getMinHeight() + 1;
|
||||
int noFluid = engine.getHeight(wX, wZ, true) + engine.getMinHeight() + 1;
|
||||
SET_HEIGHT.invoke(ocean, x, z, Math.min(noFluid, ocean.getFirstAvailable(x, z)));
|
||||
SET_HEIGHT.invoke(surface, x, z, Math.min(noAir, surface.getFirstAvailable(x, z)));
|
||||
SET_HEIGHT.invoke(motion, x, z, Math.min(noAir, motion.getFirstAvailable(x, z)));
|
||||
SET_HEIGHT.invoke(motionNoLeaves, x, z, Math.min(noAir, motionNoLeaves.getFirstAvailable(x, z)));
|
||||
int oceanHeight = ocean.getFirstAvailable(x, z);
|
||||
int surfaceHeight = surface.getFirstAvailable(x, z);
|
||||
int motionHeight = motion.getFirstAvailable(x, z);
|
||||
int motionNoLeavesHeight = motionNoLeaves.getFirstAvailable(x, z);
|
||||
if (noFluid > oceanHeight) {
|
||||
SET_HEIGHT.invoke(ocean, x, z, noFluid);
|
||||
}
|
||||
if (noAir > surfaceHeight) {
|
||||
SET_HEIGHT.invoke(surface, x, z, noAir);
|
||||
}
|
||||
if (noAir > motionHeight) {
|
||||
SET_HEIGHT.invoke(motion, x, z, noAir);
|
||||
}
|
||||
if (noAir > motionNoLeavesHeight) {
|
||||
SET_HEIGHT.invoke(motionNoLeaves, x, z, noAir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < list.size(); j++) {
|
||||
Structure structure = list.get(j);
|
||||
random.setFeatureSeed(i, j, structure.step().ordinal());
|
||||
Supplier<String> supplier = () -> structures.getResourceKey(structure).map(Object::toString).orElseGet(structure::toString);
|
||||
List<StructureStart> starts = new ArrayList<>(structureManager.startsForStructure(chunkAccess.getPos(), structure -> true));
|
||||
starts.sort(Comparator.comparingInt(start -> structureOrder.getOrDefault(start.getStructure(), Integer.MAX_VALUE)));
|
||||
|
||||
int seededStructureIndex = Integer.MIN_VALUE;
|
||||
for (int j = 0; j < starts.size(); j++) {
|
||||
StructureStart start = starts.get(j);
|
||||
Structure structure = start.getStructure();
|
||||
int structureIndex = structureOrder.getOrDefault(structure, j);
|
||||
if (structureIndex != seededStructureIndex) {
|
||||
random.setFeatureSeed(i, structureIndex, structure.step().ordinal());
|
||||
seededStructureIndex = structureIndex;
|
||||
}
|
||||
Supplier<String> supplier = () -> structureRegistry.getResourceKey(structure).map(Object::toString).orElseGet(structure::toString);
|
||||
|
||||
try {
|
||||
level.setCurrentlyGenerating(supplier);
|
||||
structureManager.startsForStructure(sectionPos, structure).forEach((start) -> start.placeInChunk(level, structureManager, this, random, getWritableArea(chunkAccess), chunkAccess.getPos()));
|
||||
start.placeInChunk(level, structureManager, this, random, getWritableArea(chunkAccess), chunkAccess.getPos());
|
||||
} catch (Exception exception) {
|
||||
CrashReport crashReport = CrashReport.forThrowable(exception, "Feature placement");
|
||||
CrashReportCategory category = crashReport.addCategory("Feature");
|
||||
@@ -210,6 +235,35 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
return new BoundingBox(minX, minY, minZ, minX + 15, maxY, minZ + 15);
|
||||
}
|
||||
|
||||
private Map<Structure, Integer> getStructureOrder(Registry<Structure> structureRegistry) {
|
||||
Map<Structure, Integer> localOrder = cachedStructureOrder;
|
||||
Registry<Structure> localRegistry = cachedStructureRegistry;
|
||||
if (localRegistry == structureRegistry && localOrder != null) {
|
||||
return localOrder;
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
Map<Structure, Integer> synchronizedOrder = cachedStructureOrder;
|
||||
Registry<Structure> synchronizedRegistry = cachedStructureRegistry;
|
||||
if (synchronizedRegistry == structureRegistry && synchronizedOrder != null) {
|
||||
return synchronizedOrder;
|
||||
}
|
||||
|
||||
List<Structure> sortedStructures = structureRegistry.stream()
|
||||
.sorted(Comparator.comparingInt(structure -> structure.step().ordinal()))
|
||||
.toList();
|
||||
Map<Structure, Integer> builtOrder = new IdentityHashMap<>(sortedStructures.size());
|
||||
for (int index = 0; index < sortedStructures.size(); index++) {
|
||||
Structure structure = sortedStructures.get(index);
|
||||
builtOrder.put(structure, index);
|
||||
}
|
||||
|
||||
cachedStructureRegistry = structureRegistry;
|
||||
cachedStructureOrder = builtOrder;
|
||||
return builtOrder;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void spawnOriginalMobs(WorldGenRegion regionlimitedworldaccess) {
|
||||
delegate.spawnOriginalMobs(regionlimitedworldaccess);
|
||||
|
||||
@@ -727,6 +727,48 @@ public class NMSBinding implements INMSBinding {
|
||||
return keys;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KMap<String, KList<String>> getVanillaStructureBiomeTags() {
|
||||
KMap<String, KList<String>> tags = new KMap<>();
|
||||
|
||||
Registry<net.minecraft.world.level.biome.Biome> registry = registry().lookup(Registries.BIOME).orElse(null);
|
||||
if (registry == null) {
|
||||
return tags;
|
||||
}
|
||||
|
||||
registry.getTags().forEach(named -> {
|
||||
TagKey<net.minecraft.world.level.biome.Biome> tagKey = named.key();
|
||||
Identifier location = tagKey.location();
|
||||
if (!"minecraft".equals(location.getNamespace())) {
|
||||
return;
|
||||
}
|
||||
|
||||
String path = location.getPath();
|
||||
if (!path.startsWith("has_structure/")) {
|
||||
return;
|
||||
}
|
||||
|
||||
KList<String> values = new KList<>();
|
||||
named.stream().forEach(holder -> {
|
||||
net.minecraft.world.level.biome.Biome biome = holder.value();
|
||||
Identifier biomeLocation = registry.getKey(biome);
|
||||
if (biomeLocation == null) {
|
||||
return;
|
||||
}
|
||||
if ("minecraft".equals(biomeLocation.getNamespace())) {
|
||||
values.add(biomeLocation.toString());
|
||||
}
|
||||
});
|
||||
|
||||
KList<String> uniqueValues = values.removeDuplicates();
|
||||
if (!uniqueValues.isEmpty()) {
|
||||
tags.put(path, uniqueValues);
|
||||
}
|
||||
});
|
||||
|
||||
return tags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean missingDimensionTypes(String... keys) {
|
||||
var type = registry().lookupOrThrow(Registries.DIMENSION_TYPE);
|
||||
|
||||
Reference in New Issue
Block a user