This commit is contained in:
Brian Neumann-Fopiano
2026-06-13 22:03:58 -04:00
parent ad6732812e
commit b0a5d97e9c
15 changed files with 64 additions and 1871 deletions
@@ -1,401 +0,0 @@
package art.arcane.iris.core.nms.v1_21_R7;
import com.mojang.serialization.MapCodec;
import art.arcane.iris.spi.IrisLogging;
import art.arcane.iris.engine.data.cache.AtomicCache;
import art.arcane.iris.engine.framework.Engine;
import art.arcane.iris.engine.object.IrisBiome;
import art.arcane.iris.engine.object.IrisBiomeCustom;
import art.arcane.volmlib.util.collection.KMap;
import art.arcane.volmlib.util.math.RNG;
import net.minecraft.core.Holder;
import net.minecraft.core.Registry;
import net.minecraft.core.RegistryAccess;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.Identifier;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeSource;
import net.minecraft.world.level.biome.Climate;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.craftbukkit.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 KMap<String, Holder<Biome>> customBiomes;
private final Holder<Biome> fallbackBiome;
private final ConcurrentHashMap<Long, Holder<Biome>> noiseBiomeCache = new ConcurrentHashMap<>();
private final ConcurrentHashMap<Long, Holder<Biome>> structureBiomeCache = 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.fallbackBiome = resolveFallbackBiome(this.biomeRegistry, this.biomeCustomRegistry);
this.customBiomes = fillCustomBiomes(this.biomeCustomRegistry, engine, this.fallbackBiome);
}
private static List<Holder<Biome>> getAllBiomes(Registry<Biome> customRegistry, Registry<Biome> registry, Engine engine, Holder<Biome> fallback) {
LinkedHashSet<Holder<Biome>> biomes = new LinkedHashSet<>();
if (fallback != null) {
biomes.add(fallback);
}
for (IrisBiome i : engine.getAllBiomes()) {
Holder<Biome> vanillaHolder = NMSBinding.biomeToBiomeBase(registry, i.getVanillaDerivative());
if (vanillaHolder != null) {
biomes.add(vanillaHolder);
} else if (!i.isCustom() && fallback != null) {
biomes.add(fallback);
}
if (i.isCustom()) {
for (IrisBiomeCustom j : i.getCustomDerivitives()) {
Holder<Biome> customHolder = resolveCustomBiomeHolder(customRegistry, engine, j.getId());
if (customHolder != null) {
biomes.add(customHolder);
} else if (fallback != null) {
biomes.add(fallback);
}
}
}
}
return new ArrayList<>(biomes);
}
private static Object getFor(Class<?> type, Object source) {
Object o = fieldFor(type, source);
if (o != null) {
return o;
}
return invokeFor(type, source);
}
private static Object fieldFor(Class<?> returns, Object in) {
return fieldForClass(returns, in.getClass(), in);
}
private static Object invokeFor(Class<?> returns, Object in) {
for (Method i : in.getClass().getMethods()) {
if (i.getReturnType().equals(returns)) {
i.setAccessible(true);
try {
IrisLogging.debug("[NMS] Found " + returns.getSimpleName() + " in " + in.getClass().getSimpleName() + "." + i.getName() + "()");
return i.invoke(in);
} catch (Throwable e) {
e.printStackTrace();
}
}
}
return null;
}
@SuppressWarnings("unchecked")
private static <T> T fieldForClass(Class<T> returnType, Class<?> sourceType, Object in) {
for (Field i : sourceType.getDeclaredFields()) {
if (i.getType().equals(returnType)) {
i.setAccessible(true);
try {
IrisLogging.debug("[NMS] Found " + returnType.getSimpleName() + " in " + sourceType.getSimpleName() + "." + i.getName());
return (T) i.get(in);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
return null;
}
@Override
protected Stream<Holder<Biome>> collectPossibleBiomes() {
return getAllBiomes(
((RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer()))
.lookup(Registries.BIOME).orElse(null),
((CraftWorld) engine.getWorld().realWorld()).getHandle().registryAccess().lookup(Registries.BIOME).orElse(null),
engine,
fallbackBiome).stream();
}
private KMap<String, Holder<Biome>> fillCustomBiomes(Registry<Biome> customRegistry, Engine engine, Holder<Biome> fallback) {
KMap<String, Holder<Biome>> m = new KMap<>();
if (customRegistry == null) {
return m;
}
for (IrisBiome i : engine.getAllBiomes()) {
if (i.isCustom()) {
for (IrisBiomeCustom j : i.getCustomDerivitives()) {
Holder<Biome> holder = resolveCustomBiomeHolder(customRegistry, engine, j.getId());
if (holder == null) {
if (fallback != null) {
m.put(j.getId(), fallback);
}
IrisLogging.error("Cannot find biome for IrisBiomeCustom " + j.getId() + " from engine " + engine.getName());
continue;
}
m.put(j.getId(), holder);
}
}
}
return m;
}
private RegistryAccess registry() {
return registryAccess.aquire(() -> (RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer()));
}
@Override
protected MapCodec<? extends BiomeSource> codec() {
throw new UnsupportedOperationException("Not supported");
}
@Override
public Holder<Biome> getNoiseBiome(int x, int y, int z, Climate.Sampler sampler) {
long cacheKey = packNoiseKey(x, y, z);
Holder<Biome> cachedHolder = structureBiomeCache.get(cacheKey);
if (cachedHolder != null) {
return cachedHolder;
}
Holder<Biome> resolvedHolder = resolveStructureBiomeHolder(x, y, z);
Holder<Biome> existingHolder = structureBiomeCache.putIfAbsent(cacheKey, resolvedHolder);
if (existingHolder != null) {
return existingHolder;
}
if (structureBiomeCache.size() > NOISE_BIOME_CACHE_MAX) {
structureBiomeCache.clear();
}
return resolvedHolder;
}
public Holder<Biome> getVisibleNoiseBiome(int x, int y, int z, Climate.Sampler sampler) {
long cacheKey = packNoiseKey(x, y, z);
Holder<Biome> cachedHolder = noiseBiomeCache.get(cacheKey);
if (cachedHolder != null) {
return cachedHolder;
}
Holder<Biome> resolvedHolder = resolveVisibleBiomeHolder(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> resolveStructureBiomeHolder(int x, int y, int z) {
BiomeResolution resolution = resolveBiomeResolution(x, y, z);
if (resolution == null) {
return getFallbackBiome();
}
Holder<Biome> holder = NMSBinding.biomeToBiomeBase(biomeRegistry, resolution.irisBiome.getVanillaDerivative());
if (holder != null) {
return holder;
}
if (resolution.irisBiome.isCustom()) {
return resolveCustomHolder(resolution);
}
return getFallbackBiome();
}
private Holder<Biome> resolveVisibleBiomeHolder(int x, int y, int z) {
BiomeResolution resolution = resolveBiomeResolution(x, y, z);
if (resolution == null) {
return getFallbackBiome();
}
if (resolution.irisBiome.isCustom()) {
return resolveCustomHolder(resolution);
}
org.bukkit.block.Biome vanillaBiome = resolution.underground
? resolution.irisBiome.getGroundBiome(resolution.rng, resolution.blockX, resolution.blockY, resolution.blockZ)
: resolution.irisBiome.getSkyBiome(resolution.rng, resolution.blockX, resolution.blockY, resolution.blockZ);
Holder<Biome> holder = NMSBinding.biomeToBiomeBase(biomeRegistry, vanillaBiome);
if (holder != null) {
return holder;
}
return getFallbackBiome();
}
private Holder<Biome> resolveCustomHolder(BiomeResolution resolution) {
IrisBiomeCustom customBiome = resolution.irisBiome.getCustomBiome(resolution.rng, resolution.blockX, resolution.blockY, resolution.blockZ);
if (customBiome != null) {
Holder<Biome> holder = customBiomes.get(customBiome.getId());
if (holder != null) {
return holder;
}
}
return getFallbackBiome();
}
private BiomeResolution resolveBiomeResolution(int x, int y, int z) {
if (engine == null || engine.isClosed()) {
return null;
}
if (engine.getComplex() == null) {
return null;
}
int blockX = x << 2;
int blockZ = z << 2;
int blockY = y << 2;
int worldMinHeight = engine.getWorld().minHeight();
int internalY = blockY - worldMinHeight;
int surfaceInternalY = engine.getComplex().getHeightStream().get(blockX, blockZ).intValue();
int caveSwitchInternalY = Math.max(-8 - worldMinHeight, 40);
boolean deepUnderground = internalY <= caveSwitchInternalY;
boolean belowSurface = internalY <= surfaceInternalY - 8;
boolean underground = deepUnderground && belowSurface;
IrisBiome irisBiome = underground
? engine.getCaveBiome(blockX, internalY, blockZ)
: engine.getComplex().getTrueBiomeStream().get(blockX, blockZ);
if (irisBiome == null && underground) {
irisBiome = engine.getComplex().getTrueBiomeStream().get(blockX, blockZ);
}
if (irisBiome == null) {
return null;
}
RNG noiseRng = new RNG(seed
^ (((long) blockX) * 341873128712L)
^ (((long) blockY) * 132897987541L)
^ (((long) blockZ) * 42317861L));
return new BiomeResolution(irisBiome, underground, blockX, blockY, blockZ, noiseRng);
}
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) {
if (customRegistry == null || engine == null || customBiomeId == null || customBiomeId.isBlank()) {
return null;
}
Identifier resourceLocation = Identifier.fromNamespaceAndPath(
engine.getDimension().getLoadKey().toLowerCase(java.util.Locale.ROOT),
customBiomeId.toLowerCase(java.util.Locale.ROOT)
);
Biome biome = customRegistry.getValue(resourceLocation);
if (biome == null) {
return null;
}
Optional<ResourceKey<Biome>> optionalBiomeKey = customRegistry.getResourceKey(biome);
if (optionalBiomeKey.isEmpty()) {
return null;
}
Optional<Holder.Reference<Biome>> optionalReferenceHolder = customRegistry.get(optionalBiomeKey.get());
if (optionalReferenceHolder.isEmpty()) {
return null;
}
return optionalReferenceHolder.get();
}
private static Holder<Biome> resolveFallbackBiome(Registry<Biome> registry, Registry<Biome> customRegistry) {
Holder<Biome> plains = NMSBinding.biomeToBiomeBase(registry, org.bukkit.block.Biome.PLAINS);
if (plains != null) {
return plains;
}
Holder<Biome> vanilla = firstHolder(registry);
if (vanilla != null) {
return vanilla;
}
return firstHolder(customRegistry);
}
private static Holder<Biome> firstHolder(Registry<Biome> registry) {
if (registry == null) {
return null;
}
for (Biome biome : registry) {
Optional<ResourceKey<Biome>> optionalBiomeKey = registry.getResourceKey(biome);
if (optionalBiomeKey.isEmpty()) {
continue;
}
Optional<Holder.Reference<Biome>> optionalHolder = registry.get(optionalBiomeKey.get());
if (optionalHolder.isPresent()) {
return optionalHolder.get();
}
}
return null;
}
private static final class BiomeResolution {
private final IrisBiome irisBiome;
private final boolean underground;
private final int blockX;
private final int blockY;
private final int blockZ;
private final RNG rng;
private BiomeResolution(IrisBiome irisBiome, boolean underground, int blockX, int blockY, int blockZ, RNG rng) {
this.irisBiome = irisBiome;
this.underground = underground;
this.blockX = blockX;
this.blockY = blockY;
this.blockZ = blockZ;
this.rng = rng;
}
}
}
@@ -1,407 +0,0 @@
package art.arcane.iris.core.nms.v1_21_R7;
import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.MapCodec;
import art.arcane.iris.spi.IrisLogging;
import art.arcane.iris.engine.framework.Engine;
import art.arcane.iris.engine.object.IrisImportedStructureControl;
import art.arcane.iris.util.common.reflect.WrappedField;
import art.arcane.iris.util.common.reflect.WrappedReturningMethod;
import net.minecraft.core.*;
import net.minecraft.resources.Identifier;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.WorldGenRegion;
import net.minecraft.util.random.WeightedList;
import net.minecraft.world.entity.MobCategory;
import net.minecraft.world.level.*;
import net.minecraft.world.level.biome.*;
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.chunk.ChunkGeneratorStructureState;
import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.world.level.levelgen.*;
import net.minecraft.world.level.levelgen.blending.Blender;
import net.minecraft.world.level.levelgen.structure.Structure;
import net.minecraft.world.level.levelgen.structure.StructureSet;
import net.minecraft.world.level.levelgen.structure.StructureStart;
import net.minecraft.world.level.levelgen.structure.BoundingBox;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
import net.minecraft.core.registries.Registries;
import java.util.stream.Collectors;
import art.arcane.iris.engine.framework.IrisStructureLocator;
import org.bukkit.World;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.craftbukkit.generator.CustomChunkGenerator;
import org.spigotmc.SpigotWorldConfig;
import javax.annotation.Nullable;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.*;
import java.util.concurrent.CompletableFuture;
public class IrisChunkGenerator extends CustomChunkGenerator {
private static final WrappedField<ChunkGenerator, BiomeSource> BIOME_SOURCE;
private static final WrappedReturningMethod<Heightmap, Object> SET_HEIGHT;
private final ChunkGenerator delegate;
private final Engine engine;
private final CustomBiomeSource customBiomeSource;
private volatile Set<String> reachableStructureKeysCache;
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
this(delegate, engine, world, new CustomBiomeSource(seed, engine, world));
}
private IrisChunkGenerator(ChunkGenerator delegate, Engine engine, World world, CustomBiomeSource customBiomeSource) {
super(((CraftWorld) world).getHandle(), edit(delegate, customBiomeSource), world.getGenerator());
this.delegate = delegate;
this.engine = engine;
this.customBiomeSource = customBiomeSource;
}
@Override
public @Nullable Pair<BlockPos, Holder<Structure>> findNearestMapStructure(ServerLevel level, HolderSet<Structure> holders, BlockPos pos, int radius, boolean findUnexplored) {
try {
Registry<Structure> registry = level.registryAccess().lookupOrThrow(Registries.STRUCTURE);
BlockPos best = null;
Holder<Structure> bestHolder = null;
long bestDist = Long.MAX_VALUE;
for (Holder<Structure> holder : holders) {
Object id = registry.getKey(holder.value());
if (id == null) {
continue;
}
int[] at = IrisStructureLocator.locate(engine, id.toString(), pos.getX(), pos.getZ(), Math.max(1, radius));
if (at == null) {
continue;
}
long dx = (long) at[0] - pos.getX();
long dz = (long) at[2] - pos.getZ();
long d = dx * dx + dz * dz;
if (d < bestDist) {
bestDist = d;
best = new BlockPos(at[0], at[1], at[2]);
bestHolder = holder;
}
}
if (best != null) {
return Pair.of(best, bestHolder);
}
} catch (Throwable e) {
IrisLogging.reportError(e);
}
if (!importedControl().active()) {
return null;
}
HolderSet<Structure> reachable = filterReachableStructures(level, holders);
if (reachable == null || reachable.size() == 0) {
return null;
}
try {
return delegate.findNearestMapStructure(level, reachable, pos, radius, findUnexplored);
} catch (Throwable e) {
IrisLogging.error("Vanilla structure locate failed near " + pos.getX() + ", " + pos.getZ() + ": " + e);
IrisLogging.reportError(e);
return null;
}
}
private HolderSet<Structure> filterReachableStructures(ServerLevel level, HolderSet<Structure> holders) {
Set<String> reachable = reachableStructureKeysCache;
if (reachable == null) {
reachable = VanillaStructureBiomes.reachableStructureKeys(level, delegate.getBiomeSource());
reachableStructureKeysCache = reachable;
}
if (reachable.isEmpty()) {
return holders;
}
Registry<Structure> registry = level.registryAccess().lookupOrThrow(Registries.STRUCTURE);
List<Holder<Structure>> kept = new ArrayList<>();
for (Holder<Structure> holder : holders) {
Object id = registry.getKey(holder.value());
if (id != null && reachable.contains(id.toString())) {
kept.add(holder);
}
}
if (kept.size() == holders.size()) {
return holders;
}
return HolderSet.direct(kept);
}
@Override
protected MapCodec<? extends ChunkGenerator> codec() {
return MapCodec.unit(null);
}
@Override
public ChunkGenerator getDelegate() {
if (delegate instanceof CustomChunkGenerator chunkGenerator)
return chunkGenerator.getDelegate();
return delegate;
}
@Override
public int getMinY() {
return delegate.getMinY();
}
@Override
public int getSeaLevel() {
return delegate.getSeaLevel();
}
@Override
public void createStructures(RegistryAccess registryAccess, ChunkGeneratorStructureState structureState, StructureManager structureManager, ChunkAccess access, StructureTemplateManager templateManager, ResourceKey<Level> levelKey) {
if (!importedControl().active()) {
return;
}
super.createStructures(registryAccess, structureState, structureManager, access, templateManager, levelKey);
}
private IrisImportedStructureControl importedControl() {
return engine.getDimension().getImportedStructures();
}
@Override
public ChunkGeneratorStructureState createState(HolderLookup<StructureSet> holderlookup, RandomState randomstate, long i, SpigotWorldConfig conf) {
return delegate.createState(holderlookup, randomstate, i, conf);
}
@Override
public void createReferences(WorldGenLevel generatoraccessseed, StructureManager structuremanager, ChunkAccess ichunkaccess) {
delegate.createReferences(generatoraccessseed, structuremanager, ichunkaccess);
}
@Override
public CompletableFuture<ChunkAccess> createBiomes(RandomState randomstate, Blender blender, StructureManager structuremanager, ChunkAccess ichunkaccess) {
ichunkaccess.fillBiomesFromNoise(customBiomeSource::getVisibleNoiseBiome, randomstate.sampler());
return CompletableFuture.completedFuture(ichunkaccess);
}
@Override
public void buildSurface(WorldGenRegion regionlimitedworldaccess, StructureManager structuremanager, RandomState randomstate, ChunkAccess ichunkaccess) {
delegate.buildSurface(regionlimitedworldaccess, structuremanager, randomstate, ichunkaccess);
}
@Override
public void applyCarvers(WorldGenRegion regionlimitedworldaccess, long seed, RandomState randomstate, BiomeManager biomemanager, StructureManager structuremanager, ChunkAccess ichunkaccess) {
delegate.applyCarvers(regionlimitedworldaccess, seed, randomstate, biomemanager, structuremanager, ichunkaccess);
}
@Override
public CompletableFuture<ChunkAccess> fillFromNoise(Blender blender, RandomState randomstate, StructureManager structuremanager, ChunkAccess ichunkaccess) {
return delegate.fillFromNoise(blender, randomstate, structuremanager, ichunkaccess);
}
@Override
public WeightedList<MobSpawnSettings.SpawnerData> getMobsAt(Holder<Biome> holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) {
return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition);
}
@Override
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) {
applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, true);
}
@Override
public void addDebugScreenInfo(List<String> list, RandomState randomstate, BlockPos blockposition) {
delegate.addDebugScreenInfo(list, randomstate, blockposition);
}
@Override
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager, boolean vanilla) {
addVanillaDecorations(generatoraccessseed, ichunkaccess, structuremanager);
if (importedControl().active()) {
placeVanillaStructures(generatoraccessseed, ichunkaccess, structuremanager);
}
delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, false);
}
private void placeVanillaStructures(WorldGenLevel world, ChunkAccess chunk, StructureManager structureManager) {
if (!structureManager.shouldGenerateStructures()) {
return;
}
ChunkPos chunkPos = chunk.getPos();
SectionPos sectionPos = SectionPos.of(chunkPos, world.getMinSectionY());
BlockPos origin = sectionPos.origin();
Registry<Structure> registry = world.registryAccess().lookupOrThrow(Registries.STRUCTURE);
Map<Integer, List<Structure>> byStep = registry.stream().collect(Collectors.groupingBy(s -> s.step().ordinal()));
WorldgenRandom random = new WorldgenRandom(new XoroshiroRandomSource(RandomSupport.generateUniqueSeed()));
long decoSeed = random.setDecorationSeed(world.getSeed(), origin.getX(), origin.getZ());
BoundingBox area = writableArea(chunk);
int steps = GenerationStep.Decoration.values().length;
IrisImportedStructureControl control = importedControl();
for (int step = 0; step < steps; step++) {
int index = 0;
for (Structure structure : byStep.getOrDefault(step, List.of())) {
Object id = registry.getKey(structure);
String structureId = id == null ? null : id.toString();
if (control.shouldGenerate(structureId) && !IrisStructureLocator.suppressesVanilla(engine, structureId)) {
random.setFeatureSeed(decoSeed, index, step);
int[] offset = control.resolveOffset(structureId, isUndergroundStep(structure.step()));
boolean shifted = offset[0] != 0 || offset[1] != 0 || offset[2] != 0;
WorldGenLevel target = shifted ? shiftedLevel(world, offset[0], offset[1], offset[2]) : world;
BoundingBox placeArea = shifted
? new BoundingBox(area.minX() - offset[0], area.minY(), area.minZ() - offset[2], area.maxX() - offset[0], area.maxY(), area.maxZ() - offset[2])
: area;
try {
structureManager.startsForStructure(sectionPos, structure)
.forEach(start -> start.placeInChunk(target, structureManager, this, random, placeArea, chunkPos));
} catch (Throwable e) {
IrisLogging.reportError(e);
}
}
index++;
}
}
}
private static boolean isUndergroundStep(GenerationStep.Decoration step) {
return step == GenerationStep.Decoration.UNDERGROUND_STRUCTURES
|| step == GenerationStep.Decoration.STRONGHOLDS;
}
private WorldGenLevel shiftedLevel(WorldGenLevel world, int dx, int dy, int dz) {
return (WorldGenLevel) Proxy.newProxyInstance(
WorldGenLevel.class.getClassLoader(),
new Class<?>[]{WorldGenLevel.class},
(proxy, method, args) -> {
if (args != null) {
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof BlockPos bp) {
args[i] = new BlockPos(bp.getX() + dx, bp.getY() + dy, bp.getZ() + dz);
}
}
}
try {
return method.invoke(world, args);
} catch (InvocationTargetException e) {
throw e.getCause();
}
});
}
private BoundingBox writableArea(ChunkAccess chunk) {
ChunkPos cp = chunk.getPos();
int i = cp.getMinBlockX();
int j = cp.getMinBlockZ();
int minY = getMinY() + 1;
int maxY = getMinY() + engine.getHeight() - 1;
return new BoundingBox(i, minY, j, i + 15, maxY, j + 15);
}
@Override
public void addVanillaDecorations(WorldGenLevel level, ChunkAccess chunkAccess, StructureManager structureManager) {
SectionPos sectionPos = SectionPos.of(chunkAccess.getPos(), level.getMinSectionY());
BlockPos blockPos = sectionPos.origin();
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++) {
int wX = x + blockPos.getX();
int wZ = z + blockPos.getZ();
int terrainTop = engine.getHeight(wX, wZ, false) + engine.getMinHeight() + 1;
int terrainNoFluid = engine.getHeight(wX, wZ, true) + engine.getMinHeight() + 1;
SET_HEIGHT.invoke(ocean, x, z, terrainNoFluid);
SET_HEIGHT.invoke(surface, x, z, terrainTop);
SET_HEIGHT.invoke(motion, x, z, terrainTop);
SET_HEIGHT.invoke(motionNoLeaves, x, z, terrainTop);
}
}
Heightmap.primeHeightmaps(chunkAccess, ChunkStatus.FINAL_HEIGHTMAPS);
}
@Override
public void spawnOriginalMobs(WorldGenRegion regionlimitedworldaccess) {
delegate.spawnOriginalMobs(regionlimitedworldaccess);
}
@Override
public int getSpawnHeight(LevelHeightAccessor levelheightaccessor) {
return delegate.getSpawnHeight(levelheightaccessor);
}
@Override
public int getGenDepth() {
return delegate.getGenDepth();
}
@Override
public int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
return levelheightaccessor.getMinY() + engine.getHeight(i, j, !heightmap_type.isOpaque().test(Blocks.WATER.defaultBlockState())) + 1;
}
@Override
public NoiseColumn getBaseColumn(int i, int j, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
int block = engine.getHeight(i, j, true);
int water = engine.getHeight(i, j, false);
BlockState[] column = new BlockState[levelheightaccessor.getHeight()];
for (int k = 0; k < column.length; k++) {
if (k <= block) column[k] = Blocks.STONE.defaultBlockState();
else if (k <= water) column[k] = Blocks.WATER.defaultBlockState();
else column[k] = Blocks.AIR.defaultBlockState();
}
return new NoiseColumn(levelheightaccessor.getMinY(), column);
}
@Override
public Optional<ResourceKey<MapCodec<? extends ChunkGenerator>>> getTypeNameForDataFixer() {
return delegate.getTypeNameForDataFixer();
}
@Override
public void validate() {
delegate.validate();
}
static {
Field biomeSource = null;
for (Field field : ChunkGenerator.class.getDeclaredFields()) {
if (!field.getType().equals(BiomeSource.class))
continue;
biomeSource = field;
break;
}
if (biomeSource == null)
throw new RuntimeException("Could not find biomeSource field in ChunkGenerator!");
Method setHeight = null;
for (Method method : Heightmap.class.getDeclaredMethods()) {
var types = method.getParameterTypes();
if (types.length != 3 || !Arrays.equals(types, new Class<?>[]{int.class, int.class, int.class})
|| !method.getReturnType().equals(void.class))
continue;
setHeight = method;
break;
}
if (setHeight == null)
throw new RuntimeException("Could not find setHeight method in Heightmap!");
BIOME_SOURCE = new WrappedField<>(ChunkGenerator.class, biomeSource.getName());
SET_HEIGHT = new WrappedReturningMethod<>(Heightmap.class, setHeight.getName(), setHeight.getParameterTypes());
}
private static ChunkGenerator edit(ChunkGenerator generator, BiomeSource source) {
try {
BIOME_SOURCE.set(generator, source);
if (generator instanceof CustomChunkGenerator custom)
BIOME_SOURCE.set(custom.getDelegate(), source);
return generator;
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
@@ -1,931 +0,0 @@
package art.arcane.iris.core.nms.v1_21_R7;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import art.arcane.iris.spi.IrisLogging;
import art.arcane.iris.core.nms.INMSBinding;
import art.arcane.iris.core.nms.container.BiomeColor;
import art.arcane.iris.core.nms.container.Pair;
import art.arcane.iris.core.nms.container.BlockProperty;
import art.arcane.iris.core.nms.datapack.DataVersion;
import art.arcane.iris.engine.data.cache.AtomicCache;
import art.arcane.iris.engine.framework.Engine;
import art.arcane.iris.engine.platform.PlatformChunkGenerator;
import art.arcane.iris.util.project.agent.Agent;
import art.arcane.volmlib.util.collection.KList;
import art.arcane.volmlib.util.collection.KMap;
import art.arcane.iris.util.common.format.C;
import art.arcane.iris.util.project.hunk.Hunk;
import art.arcane.volmlib.util.json.JSONObject;
import art.arcane.volmlib.util.mantle.runtime.Mantle;
import art.arcane.volmlib.util.matter.Matter;
import art.arcane.volmlib.util.math.Vector3d;
import art.arcane.volmlib.util.matter.MatterBiomeInject;
import art.arcane.iris.util.nbt.common.mca.NBTWorld;
import art.arcane.volmlib.util.nbt.mca.palette.*;
import art.arcane.volmlib.util.nbt.tag.CompoundTag;
import art.arcane.iris.util.common.scheduling.J;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.shorts.ShortList;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.matcher.ElementMatchers;
import net.minecraft.core.*;
import net.minecraft.core.Registry;
import net.minecraft.core.component.DataComponents;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.*;
import net.minecraft.nbt.Tag;
import net.minecraft.resources.Identifier;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.commands.data.BlockDataAccessor;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.tags.TagKey;
import net.minecraft.world.attribute.EnvironmentAttributes;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.item.component.CustomData;
import net.minecraft.world.level.biome.BiomeSource;
import net.minecraft.world.level.biome.Biomes;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.Property;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.world.level.chunk.status.WorldGenContext;
import net.minecraft.world.level.dimension.LevelStem;
import net.minecraft.world.level.levelgen.FlatLevelSource;
import net.minecraft.world.level.levelgen.flat.FlatLayerInfo;
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
import net.minecraft.world.level.levelgen.structure.StructureCheck;
import net.minecraft.world.level.storage.LevelStorageSource;
import org.bukkit.*;
import org.bukkit.block.Biome;
import org.bukkit.block.data.BlockData;
import org.bukkit.craftbukkit.CraftChunk;
import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.craftbukkit.block.CraftBlockState;
import org.bukkit.craftbukkit.block.CraftBlockStates;
import org.bukkit.craftbukkit.block.data.CraftBlockData;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.craftbukkit.util.CraftMagicNumbers;
import org.bukkit.craftbukkit.util.CraftNamespacedKey;
import org.bukkit.entity.Entity;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.generator.BiomeProvider;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import java.awt.Color;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
public class NMSBinding implements INMSBinding {
private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
private final BlockData AIR = Material.AIR.createBlockData();
private final AtomicCache<MCAIdMap<net.minecraft.world.level.biome.Biome>> biomeMapCache = new AtomicCache<>();
private final AtomicBoolean injected = new AtomicBoolean();
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
private static Object getFor(Class<?> type, Object source) {
Object o = fieldFor(type, source);
if (o != null) {
return o;
}
return invokeFor(type, source);
}
private static Object invokeFor(Class<?> returns, Object in) {
for (Method i : in.getClass().getMethods()) {
if (i.getReturnType().equals(returns)) {
i.setAccessible(true);
try {
IrisLogging.debug("[NMS] Found " + returns.getSimpleName() + " in " + in.getClass().getSimpleName() + "." + i.getName() + "()");
return i.invoke(in);
} catch (Throwable e) {
e.printStackTrace();
}
}
}
return null;
}
private static Object fieldFor(Class<?> returns, Object in) {
return fieldForClass(returns, in.getClass(), in);
}
@SuppressWarnings("unchecked")
private static <T> T fieldForClass(Class<T> returnType, Class<?> sourceType, Object in) {
for (Field i : sourceType.getDeclaredFields()) {
if (i.getType().equals(returnType)) {
i.setAccessible(true);
try {
IrisLogging.debug("[NMS] Found " + returnType.getSimpleName() + " in " + sourceType.getSimpleName() + "." + i.getName());
return (T) i.get(in);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
return null;
}
private static Class<?> getClassType(Class<?> type, int ordinal) {
return type.getDeclaredClasses()[ordinal];
}
@Override
public boolean hasTile(Material material) {
return !CraftBlockState.class.equals(CraftBlockStates.getBlockStateType(material));
}
@Override
public boolean hasTile(Location l) {
return ((CraftWorld) l.getWorld()).getHandle().getBlockEntity(new BlockPos(l.getBlockX(), l.getBlockY(), l.getBlockZ())) != null;
}
@Override
@SuppressWarnings("unchecked")
public KMap<String, Object> serializeTile(Location location) {
BlockEntity e = ((CraftWorld) location.getWorld()).getHandle().getBlockEntity(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()));
if (e == null) {
return null;
}
net.minecraft.nbt.CompoundTag tag = e.saveWithoutMetadata(registry());
return (KMap<String, Object>) convertFromTag(tag, 0, 64);
}
@Contract(value = "null, _, _ -> null", pure = true)
private Object convertFromTag(Tag tag, int depth, int maxDepth) {
if (tag == null || depth > maxDepth) return null;
return switch (tag) {
case CollectionTag collection -> {
KList<Object> list = new KList<>();
for (Object i : collection) {
if (i instanceof Tag t)
list.add(convertFromTag(t, depth + 1, maxDepth));
else list.add(i);
}
yield list;
}
case net.minecraft.nbt.CompoundTag compound -> {
KMap<String, Object> map = new KMap<>();
for (String key : compound.keySet()) {
var child = compound.get(key);
if (child == null) continue;
var value = convertFromTag(child, depth + 1, maxDepth);
if (value == null) continue;
map.put(key, value);
}
yield map;
}
case NumericTag numeric -> numeric.box();
default -> tag.asString().orElse(null);
};
}
@Override
public void deserializeTile(KMap<String, Object> map, Location pos) {
if (map == null || pos == null || pos.getWorld() == null) {
return;
}
Tag converted = convertToTag(map, 0, 64);
if (!(converted instanceof net.minecraft.nbt.CompoundTag tag)) {
return;
}
var level = ((CraftWorld) pos.getWorld()).getHandle();
var blockPos = new BlockPos(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ());
if (!J.runAt(pos, () -> merge(level, blockPos, tag))) {
IrisLogging.warn("[NMS] Failed to schedule tile deserialize at " + blockPos + " in world " + pos.getWorld().getName());
}
}
private void merge(ServerLevel level, BlockPos blockPos, net.minecraft.nbt.CompoundTag tag) {
if (level == null || blockPos == null || tag == null) {
return;
}
try {
var blockEntity = level.getBlockEntity(blockPos);
if (blockEntity == null) {
IrisLogging.warn("[NMS] BlockEntity not found at " + blockPos);
var state = level.getBlockState(blockPos);
if (!state.hasBlockEntity()) {
return;
}
blockEntity = ((EntityBlock) state.getBlock())
.newBlockEntity(blockPos, state);
}
var accessor = new BlockDataAccessor(blockEntity, blockPos);
accessor.setData(accessor.getData().merge(tag));
} catch (Throwable e) {
IrisLogging.warn("[NMS] Failed to merge tile data at " + blockPos + ": " + e.getMessage());
IrisLogging.reportError(e);
}
}
private Tag convertToTag(Object object, int depth, int maxDepth) {
if (object == null || depth > maxDepth) return EndTag.INSTANCE;
return switch (object) {
case Map<?, ?> map -> {
var tag = new net.minecraft.nbt.CompoundTag();
for (var i : map.entrySet()) {
tag.put(i.getKey().toString(), convertToTag(i.getValue(), depth + 1, maxDepth));
}
yield tag;
}
case List<?> list -> {
var tag = new ListTag();
for (var i : list) {
tag.add(convertToTag(i, depth + 1, maxDepth));
}
yield tag;
}
case Byte number -> ByteTag.valueOf(number);
case Short number -> ShortTag.valueOf(number);
case Integer number -> IntTag.valueOf(number);
case Long number -> LongTag.valueOf(number);
case Float number -> FloatTag.valueOf(number);
case Double number -> DoubleTag.valueOf(number);
case String string -> StringTag.valueOf(string);
default -> EndTag.INSTANCE;
};
}
@Override
public CompoundTag serializeEntity(Entity location) {
return null;// TODO:
}
@Override
public Entity deserializeEntity(CompoundTag s, Location newPosition) {
return null;// TODO:
}
@Override
public boolean supportsCustomHeight() {
return true;
}
private RegistryAccess registry() {
return registryAccess.aquire(() -> (RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer()));
}
private Registry<net.minecraft.world.level.biome.Biome> getCustomBiomeRegistry() {
return registry().lookup(Registries.BIOME).orElse(null);
}
private Registry<Block> getBlockRegistry() {
return registry().lookup(Registries.BLOCK).orElse(null);
}
@Override
public Object getBiomeBaseFromId(int id) {
return getCustomBiomeRegistry().get(id);
}
@Override
public int getMinHeight(World world) {
return world.getMinHeight();
}
@Override
public boolean supportsCustomBiomes() {
return true;
}
@Override
public int getTrueBiomeBaseId(Object biomeBase) {
return getCustomBiomeRegistry().getId(((Holder<net.minecraft.world.level.biome.Biome>) biomeBase).value());
}
@Override
public Object getTrueBiomeBase(Location location) {
return ((CraftWorld) location.getWorld()).getHandle().getBiome(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()));
}
@Override
public String getTrueBiomeBaseKey(Location location) {
return getKeyForBiomeBase(getTrueBiomeBase(location));
}
@Override
public Object getCustomBiomeBaseFor(String mckey) {
return getCustomBiomeRegistry().getValue(net.minecraft.resources.Identifier.parse(mckey));
}
@Override
public Object getCustomBiomeBaseHolderFor(String mckey) {
return getCustomBiomeRegistry().get(getTrueBiomeBaseId(getCustomBiomeRegistry().get(net.minecraft.resources.Identifier.parse(mckey)))).orElse(null);
}
public int getBiomeBaseIdForKey(String key) {
return getCustomBiomeRegistry().getId(getCustomBiomeRegistry().get(net.minecraft.resources.Identifier.parse(key)).map(Holder::value).orElse(null));
}
@Override
public String getKeyForBiomeBase(Object biomeBase) {
return getCustomBiomeRegistry().getKey((net.minecraft.world.level.biome.Biome) biomeBase).getPath(); // something, not something:something
}
@Override
public Object getBiomeBase(World world, Biome biome) {
return biomeToBiomeBase(((CraftWorld) world).getHandle()
.registryAccess().lookup(Registries.BIOME).orElse(null), biome);
}
@Override
public Object getBiomeBase(Object registry, Biome biome) {
Object v = baseBiomeCache.get(biome);
if (v != null) {
return v;
}
//noinspection unchecked
v = biomeToBiomeBase((Registry<net.minecraft.world.level.biome.Biome>) registry, biome);
if (v == null) {
// Ok so there is this new biome name called "CUSTOM" in Paper's new releases.
// But, this does NOT exist within CraftBukkit which makes it return an error.
// So, we will just return the ID that the plains biome returns instead.
//noinspection unchecked
return biomeToBiomeBase((Registry<net.minecraft.world.level.biome.Biome>) registry, Biome.PLAINS);
}
baseBiomeCache.put(biome, v);
return v;
}
@Override
public KList<Biome> getBiomes() {
KList<Biome> biomes = new KList<>();
for (Biome biome : org.bukkit.Registry.BIOME) {
biomes.add(biome);
}
return biomes;
}
@Override
public KList<String> getStructureKeys() {
KList<String> keys = new KList<>();
try {
registry().lookupOrThrow(Registries.STRUCTURE).keySet().forEach(k -> keys.add(k.toString()));
} catch (Throwable e) {
IrisLogging.reportError(e);
}
return keys;
}
@Override
public KList<String> getStructureSetKeys() {
KList<String> keys = new KList<>();
try {
registry().lookupOrThrow(Registries.STRUCTURE_SET).keySet().forEach(k -> keys.add(k.toString()));
} catch (Throwable e) {
IrisLogging.reportError(e);
}
return keys;
}
@Override
public KList<String> getReachableStructureKeys(World world) {
KList<String> keys = new KList<>();
try {
ServerLevel level = ((CraftWorld) world).getHandle();
BiomeSource source = level.getChunkSource().getGenerator().getBiomeSource();
keys.addAll(VanillaStructureBiomes.reachableStructureKeys(level, source));
} catch (Throwable e) {
IrisLogging.reportError(e);
}
return keys;
}
@Override
public KList<String> getStructureBiomeKeys(String structureKey) {
KList<String> keys = new KList<>();
try {
keys.addAll(VanillaStructureBiomes.structureBiomeKeys(registry(), structureKey));
} catch (Throwable e) {
IrisLogging.reportError(e);
}
return keys;
}
@Override
public KList<String> getPossibleBiomeKeys(World world) {
KList<String> keys = new KList<>();
try {
ServerLevel level = ((CraftWorld) world).getHandle();
BiomeSource source = level.getChunkSource().getGenerator().getBiomeSource();
keys.addAll(VanillaStructureBiomes.possibleBiomeKeys(source));
} catch (Throwable e) {
IrisLogging.reportError(e);
}
return keys;
}
@Override
public int getBiomeId(Biome biome) {
for (World i : Bukkit.getWorlds()) {
if (i.getEnvironment().equals(World.Environment.NORMAL)) {
Registry<net.minecraft.world.level.biome.Biome> registry = ((CraftWorld) i).getHandle().registryAccess().lookup(Registries.BIOME).orElse(null);
return registry.getId((net.minecraft.world.level.biome.Biome) getBiomeBase(registry, biome));
}
}
List<Biome> biomes = new ArrayList<>();
for (Biome entry : org.bukkit.Registry.BIOME) {
biomes.add(entry);
}
int index = biomes.indexOf(biome);
return Math.max(index, 0);
}
private MCAIdMap<net.minecraft.world.level.biome.Biome> getBiomeMapping() {
return biomeMapCache.aquire(() -> new MCAIdMap<>() {
@NotNull
@Override
public Iterator<net.minecraft.world.level.biome.Biome> iterator() {
return getCustomBiomeRegistry().iterator();
}
@Override
public int getId(net.minecraft.world.level.biome.Biome paramT) {
return getCustomBiomeRegistry().getId(paramT);
}
@Override
public net.minecraft.world.level.biome.Biome byId(int paramInt) {
return (net.minecraft.world.level.biome.Biome) getBiomeBaseFromId(paramInt);
}
});
}
@NotNull
private MCABiomeContainer getBiomeContainerInterface(MCAIdMap<net.minecraft.world.level.biome.Biome> biomeMapping, MCAChunkBiomeContainer<net.minecraft.world.level.biome.Biome> base) {
return new MCABiomeContainer() {
@Override
public int[] getData() {
return base.writeBiomes();
}
@Override
public void setBiome(int x, int y, int z, int id) {
base.setBiome(x, y, z, biomeMapping.byId(id));
}
@Override
public int getBiome(int x, int y, int z) {
return biomeMapping.getId(base.getBiome(x, y, z));
}
};
}
@Override
public MCABiomeContainer newBiomeContainer(int min, int max) {
MCAChunkBiomeContainer<net.minecraft.world.level.biome.Biome> base = new MCAChunkBiomeContainer<>(getBiomeMapping(), min, max);
return getBiomeContainerInterface(getBiomeMapping(), base);
}
@Override
public MCABiomeContainer newBiomeContainer(int min, int max, int[] data) {
MCAChunkBiomeContainer<net.minecraft.world.level.biome.Biome> base = new MCAChunkBiomeContainer<>(getBiomeMapping(), min, max, data);
return getBiomeContainerInterface(getBiomeMapping(), base);
}
@Override
public int countCustomBiomes() {
AtomicInteger a = new AtomicInteger(0);
getCustomBiomeRegistry().keySet().forEach((i) -> {
if (i.getNamespace().equals("minecraft")) {
return;
}
a.incrementAndGet();
IrisLogging.debug("Custom Biome: " + i);
});
return a.get();
}
public boolean supportsDataPacks() {
return true;
}
public void setBiomes(int cx, int cz, World world, Hunk<Object> biomes) {
LevelChunk c = ((CraftWorld) world).getHandle().getChunk(cx, cz);
biomes.iterateSync((x, y, z, b) -> c.setBiome(x, y, z, (Holder<net.minecraft.world.level.biome.Biome>) b));
c.markUnsaved();
}
@Override
public MCAPaletteAccess createPalette() {
MCAIdMapper<BlockState> registry = registryCache.aquireNasty(() -> {
Field cf = IdMapper.class.getDeclaredField("tToId");
Field df = IdMapper.class.getDeclaredField("idToT");
Field bf = IdMapper.class.getDeclaredField("nextId");
cf.setAccessible(true);
df.setAccessible(true);
bf.setAccessible(true);
IdMapper<BlockState> blockData = Block.BLOCK_STATE_REGISTRY;
int b = bf.getInt(blockData);
Object2IntMap<BlockState> c = (Object2IntMap<BlockState>) cf.get(blockData);
List<BlockState> d = (List<BlockState>) df.get(blockData);
return new MCAIdMapper<BlockState>(c, d, b);
});
MCAPalette<BlockState> global = globalCache.aquireNasty(() -> new MCAGlobalPalette<>(registry, ((CraftBlockData) AIR).getState()));
MCAPalettedContainer<BlockState> container = new MCAPalettedContainer<>(global, registry,
i -> ((CraftBlockData) NBTWorld.getBlockData(i)).getState(),
i -> NBTWorld.getCompound(CraftBlockData.fromData(i)),
((CraftBlockData) AIR).getState());
return new MCAWrappedPalettedContainer<>(container,
i -> NBTWorld.getCompound(CraftBlockData.fromData(i)),
i -> ((CraftBlockData) NBTWorld.getBlockData(i)).getState());
}
@Override
public void injectBiomesFromMantle(Chunk e, Mantle<Matter> mantle) {
ChunkAccess chunk = ((CraftChunk) e).getHandle(ChunkStatus.FULL);
AtomicInteger c = new AtomicInteger();
AtomicInteger r = new AtomicInteger();
mantle.iterateChunk(e.getX(), e.getZ(), MatterBiomeInject.class, (x, y, z, b) -> {
if (b != null) {
if (b.isCustom()) {
chunk.setBiome(x, y, z, getCustomBiomeRegistry().get(b.getBiomeId()).get());
c.getAndIncrement();
} else {
chunk.setBiome(x, y, z, (Holder<net.minecraft.world.level.biome.Biome>) getBiomeBase(e.getWorld(), b.getBiome()));
r.getAndIncrement();
}
}
});
}
public ItemStack applyCustomNbt(ItemStack itemStack, KMap<String, Object> customNbt) throws IllegalArgumentException {
if (customNbt != null && !customNbt.isEmpty()) {
net.minecraft.world.item.ItemStack s = CraftItemStack.asNMSCopy(itemStack);
try {
net.minecraft.nbt.CompoundTag tag = TagParser.parseCompoundFully((new JSONObject(customNbt)).toString());
tag.merge(s.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY).copyTag());
s.set(DataComponents.CUSTOM_DATA, CustomData.of(tag));
} catch (CommandSyntaxException var5) {
throw new IllegalArgumentException(var5);
}
return CraftItemStack.asBukkitCopy(s);
} else {
return itemStack;
}
}
public void inject(long seed, Engine engine, World world) throws NoSuchFieldException, IllegalAccessException {
var chunkMap = ((CraftWorld)world).getHandle().getChunkSource().chunkMap;
var worldGenContextField = getField(chunkMap.getClass(), WorldGenContext.class);
worldGenContextField.setAccessible(true);
var worldGenContext = (WorldGenContext) worldGenContextField.get(chunkMap);
var dimensionType = chunkMap.level.dimensionTypeRegistration().unwrapKey().orElse(null);
String expectedDimensionType = "iris:" + engine.getDimension().getDimensionTypeKey();
if (dimensionType != null) {
String actualDimensionType = dimensionType.identifier().toString();
if (!dimensionType.identifier().getNamespace().equals("iris")) {
IrisLogging.error("Loaded world %s with invalid dimension type! expected=%s actual=%s", world.getName(), expectedDimensionType, actualDimensionType);
} else {
IrisLogging.debug("Loaded world " + world.getName() + " with Iris dimension type " + actualDimensionType);
}
} else {
IrisLogging.error("Loaded world %s with unknown dimension type! expected=%s", world.getName(), expectedDimensionType);
}
IrisChunkGenerator irisGenerator = new IrisChunkGenerator(worldGenContext.generator(), seed, engine, world);
var newContext = new WorldGenContext(
worldGenContext.level(), irisGenerator,
worldGenContext.structureManager(), worldGenContext.lightEngine(), worldGenContext.mainThreadExecutor(), worldGenContext.unsavedListener());
worldGenContextField.set(chunkMap, newContext);
retargetStructureCheck(((CraftWorld) world).getHandle(), irisGenerator);
}
private static void retargetStructureCheck(ServerLevel level, IrisChunkGenerator generator) throws NoSuchFieldException, IllegalAccessException {
Field structureCheckField = getField(level.getClass(), StructureCheck.class);
structureCheckField.setAccessible(true);
Object structureCheck = structureCheckField.get(level);
if (structureCheck == null) {
return;
}
Field generatorField = getField(structureCheck.getClass(), net.minecraft.world.level.chunk.ChunkGenerator.class);
generatorField.setAccessible(true);
generatorField.set(structureCheck, generator);
Field biomeSourceField = getField(structureCheck.getClass(), BiomeSource.class);
biomeSourceField.setAccessible(true);
biomeSourceField.set(structureCheck, generator.getBiomeSource());
}
public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) {
if (entity == null) {
return null;
}
try {
String descriptionId = "entity.minecraft." + entity.name().toLowerCase(Locale.ROOT);
Field[] fields = EntityType.class.getDeclaredFields();
for (Field field : fields) {
if (!Modifier.isStatic(field.getModifiers()) || !field.getType().equals(EntityType.class)) {
continue;
}
EntityType entityType = (EntityType) field.get(null);
if (entityType == null) {
continue;
}
if (descriptionId.equals(entityType.getDescriptionId())) {
return new Vector3d(entityType.getWidth(), entityType.getHeight(), entityType.getWidth());
}
}
return null;
} catch (Throwable e) {
IrisLogging.error("Unable to get entity dimensions for " + entity + "!");
IrisLogging.reportError(e);
return null;
}
}
@Override
public Entity spawnEntity(Location location, org.bukkit.entity.EntityType type, CreatureSpawnEvent.SpawnReason reason) {
if (location == null || location.getWorld() == null || type == null || type.getEntityClass() == null) {
return null;
}
return ((CraftWorld) location.getWorld()).spawn(location, type.getEntityClass(), null, reason);
}
@Override
public Color getBiomeColor(Location location, BiomeColor type) {
ServerLevel reader = ((CraftWorld) location.getWorld()).getHandle();
var pos = new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ());
var holder = reader.getBiome(pos);
var biome = holder.value();
if (biome == null) throw new IllegalArgumentException("Invalid biome: " + holder.unwrapKey().orElse(null));
var attributes = reader.environmentAttributes();
int rgba = switch (type) {
case FOG -> attributes.getValue(EnvironmentAttributes.FOG_COLOR, pos);
case WATER -> biome.getWaterColor();
case WATER_FOG -> attributes.getValue(EnvironmentAttributes.WATER_FOG_COLOR, pos);
case SKY -> attributes.getValue(EnvironmentAttributes.SKY_COLOR, pos);
case FOLIAGE -> biome.getFoliageColor();
case GRASS -> biome.getGrassColor(location.getBlockX(), location.getBlockZ());
};
if (rgba == 0) {
if (BiomeColor.FOLIAGE == type && biome.getSpecialEffects().foliageColorOverride().isEmpty())
return null;
if (BiomeColor.GRASS == type && biome.getSpecialEffects().grassColorOverride().isEmpty())
return null;
}
return new Color(rgba, true);
}
private static Field getField(Class<?> clazz, Class<?> fieldType) throws NoSuchFieldException {
try {
for (Field f : clazz.getDeclaredFields()) {
if (f.getType().equals(fieldType))
return f;
}
throw new NoSuchFieldException(fieldType.getName());
} catch (NoSuchFieldException var4) {
Class<?> superClass = clazz.getSuperclass();
if (superClass == null) {
throw var4;
} else {
return getField(superClass, fieldType);
}
}
}
public static Holder<net.minecraft.world.level.biome.Biome> biomeToBiomeBase(Registry<net.minecraft.world.level.biome.Biome> registry, Biome biome) {
if (registry == null || biome == null) {
return null;
}
NamespacedKey biomeKey = resolveBiomeKey(biome);
if (biomeKey == null) {
return null;
}
ResourceKey<net.minecraft.world.level.biome.Biome> key = ResourceKey.create(Registries.BIOME, CraftNamespacedKey.toMinecraft(biomeKey));
return registry.get(key).orElse(null);
}
private static NamespacedKey resolveBiomeKey(Biome biome) {
Object keyOrNullValue = invokeNoThrow(biome, "getKeyOrNull", new Class<?>[0]);
if (keyOrNullValue instanceof NamespacedKey namespacedKey) {
return namespacedKey;
}
Object keyOrThrowValue = invokeNoThrow(biome, "getKeyOrThrow", new Class<?>[0]);
if (keyOrThrowValue instanceof NamespacedKey namespacedKey) {
return namespacedKey;
}
Object keyValue = invokeNoThrow(biome, "getKey", new Class<?>[0]);
if (keyValue instanceof NamespacedKey namespacedKey) {
return namespacedKey;
}
return null;
}
private static Object invokeNoThrow(Object target, String methodName, Class<?>[] parameterTypes, Object... args) {
if (target == null) {
return null;
}
try {
Method method = target.getClass().getMethod(methodName, parameterTypes);
return method.invoke(target, args);
} catch (Throwable ignored) {
return null;
}
}
@Override
public DataVersion getDataVersion() {
return DataVersion.V1_21_11;
}
@Override
public int getSpawnChunkCount(World world) {
return 0;
}
@Override
public boolean missingDimensionTypes(String... keys) {
var type = registry().lookupOrThrow(Registries.DIMENSION_TYPE);
return !Arrays.stream(keys)
.map(key -> Identifier.fromNamespaceAndPath("iris", key))
.allMatch(type::containsKey);
}
@Override
public boolean injectBukkit() {
if (injected.getAndSet(true))
return true;
try {
IrisLogging.info("Injecting Bukkit");
var buddy = new ByteBuddy();
buddy.redefine(ServerLevel.class)
.visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor()
.and(ElementMatchers.takesArgument(0, MinecraftServer.class))
.and(ElementMatchers.takesArgument(5, LevelStem.class))))
.make()
.load(ServerLevel.class.getClassLoader(), Agent.installed());
for (Class<?> clazz : List.of(ChunkAccess.class, ProtoChunk.class)) {
buddy.redefine(clazz)
.visit(Advice.to(ChunkAccessAdvice.class).on(ElementMatchers.isMethod().and(ElementMatchers.takesArguments(ShortList.class, int.class))))
.make()
.load(clazz.getClassLoader(), Agent.installed());
}
return true;
} catch (Throwable e) {
IrisLogging.error(C.RED + "Failed to inject Bukkit");
e.printStackTrace();
}
return false;
}
@Override
public KMap<Material, List<BlockProperty>> getBlockProperties() {
KMap<Material, List<BlockProperty>> states = new KMap<>();
for (var block : registry().lookupOrThrow(Registries.BLOCK)) {
var state = block.defaultBlockState();
if (state == null) state = block.getStateDefinition().any();
final var finalState = state;
states.put(CraftMagicNumbers.getMaterial(block), block.getStateDefinition()
.getProperties()
.stream()
.map(p -> createProperty(p, finalState))
.toList());
}
return states;
}
private <T extends Comparable<T>> BlockProperty createProperty(Property<T> property, BlockState state) {
return new BlockProperty(property.getName(), property.getValueClass(), state.getValue(property), property.getPossibleValues(), property::getName);
}
@Override
public Object createRuntimeLevelStem(Object registryAccess, ChunkGenerator raw) {
if (!(registryAccess instanceof RegistryAccess access)) {
throw new IllegalStateException("Runtime LevelStem creation requires a RegistryAccess instance.");
}
if (!(raw instanceof PlatformChunkGenerator generator)) {
throw new IllegalStateException("Generator is not platform chunk generator!");
}
Identifier dimensionKey = Identifier.fromNamespaceAndPath("iris", generator.getTarget().getDimension().getDimensionTypeKey());
Holder.Reference<net.minecraft.world.level.dimension.DimensionType> dimensionType = access.lookupOrThrow(Registries.DIMENSION_TYPE)
.getOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, dimensionKey));
return new LevelStem(dimensionType, chunkGenerator(access));
}
private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) {
var settings = new FlatLevelGeneratorSettings(Optional.empty(), access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID), List.of());
settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR));
settings.updateLayers();
return new FlatLevelSource(settings);
}
private static class ChunkAccessAdvice {
@Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class)
static boolean enter(@Advice.This ChunkAccess access, @Advice.Argument(1) int index) {
return index >= access.getPostProcessing().length;
}
}
private static class ServerLevelAdvice {
@Advice.OnMethodEnter
static void enter(
@Advice.Argument(0) MinecraftServer server,
@Advice.Argument(2) LevelStorageSource.LevelStorageAccess levelStorageAccess,
@Advice.Argument(value = 5, readOnly = false) LevelStem levelStem
) {
if (levelStorageAccess == null)
return;
try {
String levelId = levelStorageAccess.getLevelId();
if (levelId == null || levelId.isBlank()) {
return;
}
Object generator = Class.forName("art.arcane.iris.core.lifecycle.WorldLifecycleStaging", true, Bukkit.getPluginManager().getPlugin("Iris")
.getClass()
.getClassLoader())
.getDeclaredMethod("consumeStemGenerator", String.class)
.invoke(null, levelId);
if (!(generator instanceof ChunkGenerator gen) || !gen.getClass().getPackageName().startsWith("art.arcane.iris")) {
return;
}
Object bindings = Class.forName("art.arcane.iris.core.nms.INMS", true, Bukkit.getPluginManager().getPlugin("Iris")
.getClass()
.getClassLoader())
.getDeclaredMethod("get")
.invoke(null);
if (bindings == null) {
throw new IllegalStateException("Iris failed to resolve an INMSBinding instance.");
}
java.lang.reflect.Method stemMethod = null;
for (java.lang.reflect.Method candidate : bindings.getClass().getMethods()) {
if (candidate.getName().equals("createRuntimeLevelStem") && candidate.getParameterCount() == 2) {
stemMethod = candidate;
break;
}
}
if (stemMethod == null) {
throw new IllegalStateException("Iris binding is missing createRuntimeLevelStem.");
}
Object resolvedStem = stemMethod.invoke(bindings, server.registryAccess(), gen);
if (!(resolvedStem instanceof LevelStem runtimeStem)) {
throw new IllegalStateException("Iris runtime LevelStem binding returned " + (resolvedStem == null ? "null" : resolvedStem.getClass().getName()) + ".");
}
levelStem = runtimeStem;
} catch (Throwable e) {
throw new RuntimeException("Iris failed to replace the levelStem", e instanceof InvocationTargetException ex ? ex.getCause() : e);
}
}
}
}
@@ -1,77 +0,0 @@
package art.arcane.iris.core.nms.v1_21_R7;
import net.minecraft.core.Holder;
import net.minecraft.core.Registry;
import net.minecraft.core.RegistryAccess;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.Identifier;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeSource;
import net.minecraft.world.level.levelgen.structure.Structure;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
final class VanillaStructureBiomes {
private VanillaStructureBiomes() {
}
static Set<String> possibleBiomeKeys(BiomeSource source) {
Set<String> keys = new LinkedHashSet<>();
if (source == null) {
return keys;
}
for (Holder<Biome> holder : source.possibleBiomes()) {
Optional<ResourceKey<Biome>> key = holder.unwrapKey();
if (key.isPresent()) {
keys.add(key.get().identifier().toString());
}
}
return keys;
}
static Set<String> structureBiomeKeys(RegistryAccess access, String structureKey) {
Set<String> keys = new LinkedHashSet<>();
if (access == null || structureKey == null || structureKey.isEmpty()) {
return keys;
}
Registry<Structure> registry = access.lookupOrThrow(Registries.STRUCTURE);
Structure structure = registry.getValue(Identifier.parse(structureKey));
if (structure == null) {
return keys;
}
for (Holder<Biome> holder : structure.biomes()) {
Optional<ResourceKey<Biome>> key = holder.unwrapKey();
if (key.isPresent()) {
keys.add(key.get().identifier().toString());
}
}
return keys;
}
static Set<String> reachableStructureKeys(ServerLevel level, BiomeSource source) {
Set<String> reachable = new LinkedHashSet<>();
if (level == null || source == null) {
return reachable;
}
Set<String> possible = possibleBiomeKeys(source);
if (possible.isEmpty()) {
return reachable;
}
Registry<Structure> registry = level.registryAccess().lookupOrThrow(Registries.STRUCTURE);
for (Map.Entry<ResourceKey<Structure>, Structure> entry : registry.entrySet()) {
for (Holder<Biome> holder : entry.getValue().biomes()) {
Optional<ResourceKey<Biome>> key = holder.unwrapKey();
if (key.isPresent() && possible.contains(key.get().identifier().toString())) {
reachable.add(entry.getKey().identifier().toString());
break;
}
}
}
return reachable;
}
}
-1
View File
@@ -101,7 +101,6 @@ registerCustomOutputTaskUnix('CrazyDev22LT', '/home/julian/Desktop/server/plugin
// ==============================================================
def nmsBindings = [
v1_21_R7: '1.21.11-R0.1-SNAPSHOT',
v26_1_R1: '26.1.2.build.66-stable',
]
Class nmsTypeClass = Class.forName('NMSBinding$Type')
@@ -28,13 +28,10 @@ import java.util.List;
import java.util.Set;
public class INMS {
private static final Version CURRENT = Boolean.getBoolean("iris.no-version-limit") ?
new Version(Integer.MAX_VALUE, Integer.MAX_VALUE, null) :
new Version(26, 1, null);
private static final Version CURRENT = new Version(26, 1, 2, "v26_1_R1");
private static final List<Version> REVISION = List.of(
new Version(26, 1, "v26_1_R1"),
new Version(21, 11, "v1_21_R7")
CURRENT
);
//@done
@@ -88,7 +85,7 @@ public class INMS {
MinecraftVersion detectedVersion = getMinecraftVersion();
String serverVersion = detectedVersion == null ? Bukkit.getServer().getVersion() : detectedVersion.value();
throw new IllegalStateException("Iris requires Minecraft 1.21.11 or newer. Detected server version: " + serverVersion);
throw new IllegalStateException("Iris requires Minecraft 26.1.2. Detected server version: " + serverVersion);
}
private static String getTag(List<Version> versions, String def) {
@@ -97,12 +94,8 @@ public class INMS {
return def;
}
if (detectedVersion.isNewerThan(CURRENT.major, CURRENT.minor)) {
return versions.getFirst().tag;
}
for (Version p : versions) {
if (!detectedVersion.isAtLeast(p.major, p.minor)) {
if (!detectedVersion.isSameRelease(p.major, p.minor, p.patch)) {
continue;
}
return p.tag;
@@ -155,5 +148,5 @@ public class INMS {
return codes;
}
private record Version(int major, int minor, String tag) {}
private record Version(int major, int minor, int patch, String tag) {}
}
@@ -187,7 +187,7 @@ public interface INMSBinding {
Color getBiomeColor(Location location, BiomeColor type);
default DataVersion getDataVersion() {
return DataVersion.V1_19_2;
return DataVersion.V26_1_2;
}
default int getSpawnChunkCount(World world) {
@@ -12,11 +12,13 @@ final class MinecraftVersion {
private final String value;
private final int major;
private final int minor;
private final int patch;
private MinecraftVersion(String value, int major, int minor) {
private MinecraftVersion(String value, int major, int minor, int patch) {
this.value = value;
this.major = major;
this.minor = minor;
this.patch = patch;
}
public static MinecraftVersion detect(Server server) {
@@ -92,12 +94,13 @@ final class MinecraftVersion {
if ("1".equals(parts[0])) {
int major = Integer.parseInt(parts[1]);
int minor = parts.length > 2 ? Integer.parseInt(parts[2]) : 0;
return new MinecraftVersion(input, major, minor);
return new MinecraftVersion(input, major, minor, 0);
}
int major = Integer.parseInt(parts[0]);
int minor = Integer.parseInt(parts[1]);
return new MinecraftVersion(input, major, minor);
int patch = parts.length > 2 ? Integer.parseInt(parts[2]) : 0;
return new MinecraftVersion(input, major, minor, patch);
} catch (NumberFormatException ignored) {
return null;
}
@@ -115,10 +118,28 @@ final class MinecraftVersion {
return minor;
}
public int patch() {
return patch;
}
public boolean isAtLeast(int major, int minor) {
return this.major > major || (this.major == major && this.minor >= minor);
}
public boolean isAtLeast(int major, int minor, int patch) {
if (this.major != major) {
return this.major > major;
}
if (this.minor != minor) {
return this.minor > minor;
}
return this.patch >= patch;
}
public boolean isSameRelease(int major, int minor, int patch) {
return this.major == major && this.minor == minor && this.patch == patch;
}
public boolean isNewerThan(int major, int minor) {
return this.major > major || (this.major == major && this.minor > minor);
}
@@ -2,9 +2,6 @@ package art.arcane.iris.core.nms.datapack;
import art.arcane.iris.spi.IrisLogging;
import art.arcane.iris.core.nms.INMS;
import art.arcane.iris.core.nms.datapack.v1192.DataFixerV1192;
import art.arcane.iris.core.nms.datapack.v1206.DataFixerV1206;
import art.arcane.iris.core.nms.datapack.v1213.DataFixerV1213;
import art.arcane.iris.core.nms.datapack.v1217.DataFixerV1217;
import art.arcane.volmlib.util.collection.KMap;
import lombok.AccessLevel;
@@ -16,10 +13,6 @@ import java.util.function.Supplier;
@Getter
public enum DataVersion {
UNSUPPORTED("0.0.0", 0, () -> null),
V1_19_2("1.19.2", 10, DataFixerV1192::new),
V1_20_5("1.20.6", 41, DataFixerV1206::new),
V1_21_3("1.21.3", 57, DataFixerV1213::new),
V1_21_11("1.21.11", 75, DataFixerV1217::new),
V26_1_2("26.1.2", 101, DataFixerV1217::new);
private static final KMap<DataVersion, IDataFixer> cache = new KMap<>();
@Getter(AccessLevel.NONE)
@@ -94,7 +94,7 @@ public final class Tasks {
} else if (parts.length >= 2) {
supportedVersions = parts[1];
} else {
supportedVersions = "1.21.11";
supportedVersions = "26.1.2";
}
if (!(INMS.get() instanceof NMSBinding1X)) {
@@ -22,7 +22,7 @@ import static org.mockito.Mockito.mockingDetails;
public class IrisRuntimeSchedulerModeRoutingTest {
@Test
public void autoResolvesToPaperLikeOnPurpurBranding() {
installServer("Purpur", "git-Purpur-2562 (MC: 1.21.11)");
installServer("Purpur", "git-Purpur-2562 (MC: 26.1.2)");
IrisSettings.IrisSettingsPregen pregen = new IrisSettings.IrisSettingsPregen();
pregen.runtimeSchedulerMode = IrisRuntimeSchedulerMode.AUTO;
@@ -32,7 +32,7 @@ public class IrisRuntimeSchedulerModeRoutingTest {
@Test
public void autoResolvesToFoliaWhenBrandingContainsFolia() {
installServer("Folia", "git-Folia-123 (MC: 1.21.11)");
installServer("Folia", "git-Folia-123 (MC: 26.1.2)");
IrisSettings.IrisSettingsPregen pregen = new IrisSettings.IrisSettingsPregen();
pregen.runtimeSchedulerMode = IrisRuntimeSchedulerMode.AUTO;
@@ -42,7 +42,7 @@ public class IrisRuntimeSchedulerModeRoutingTest {
@Test
public void autoResolvesToPaperLikeOnCanvasBranding() {
installServer("Canvas", "git-Canvas-101 (MC: 1.21.11)");
installServer("Canvas", "git-Canvas-101 (MC: 26.1.2)");
IrisSettings.IrisSettingsPregen pregen = new IrisSettings.IrisSettingsPregen();
pregen.runtimeSchedulerMode = IrisRuntimeSchedulerMode.AUTO;
@@ -52,7 +52,7 @@ public class IrisRuntimeSchedulerModeRoutingTest {
@Test
public void explicitModeBypassesAutoDetection() {
installServer("Purpur", "git-Purpur-2562 (MC: 1.21.11)");
installServer("Purpur", "git-Purpur-2562 (MC: 26.1.2)");
IrisSettings.IrisSettingsPregen pregen = new IrisSettings.IrisSettingsPregen();
pregen.runtimeSchedulerMode = IrisRuntimeSchedulerMode.FOLIA;
@@ -10,22 +10,22 @@ import static org.junit.Assert.assertTrue;
public class INMSBindingProbeCodesTest {
@Test
public void skipsSyntheticBukkitBindingWhenNmsIsEnabled() {
List<String> probeCodes = NmsBindingProbeSupport.getBindingProbeCodes("BUKKIT", false, List.of("v1_21_R7"));
List<String> probeCodes = NmsBindingProbeSupport.getBindingProbeCodes("BUKKIT", false, List.of("v26_1_R1"));
assertEquals(List.of("v1_21_R7"), probeCodes);
assertEquals(List.of("v26_1_R1"), probeCodes);
}
@Test
public void leavesBukkitFallbackEmptyWhenNmsIsDisabled() {
List<String> probeCodes = NmsBindingProbeSupport.getBindingProbeCodes("BUKKIT", true, List.of("v1_21_R7"));
List<String> probeCodes = NmsBindingProbeSupport.getBindingProbeCodes("BUKKIT", true, List.of("v26_1_R1"));
assertTrue(probeCodes.isEmpty());
}
@Test
public void keepsConcreteBindingCodesAsPrimaryProbe() {
List<String> probeCodes = NmsBindingProbeSupport.getBindingProbeCodes("v1_21_R7", false, List.of("v1_21_R7"));
List<String> probeCodes = NmsBindingProbeSupport.getBindingProbeCodes("v26_1_R1", false, List.of("v26_1_R1"));
assertEquals(List.of("v1_21_R7"), probeCodes);
assertEquals(List.of("v26_1_R1"), probeCodes);
}
}
@@ -18,26 +18,28 @@ public class MinecraftVersionTest {
@Test
public void detectsMinecraftVersionFromPurpurDecoratedVersion() {
Server server = mock(Server.class);
doReturn("git-Purpur-2570 (MC: 1.21.11)").when(server).getVersion();
doReturn("git-Purpur-2570 (MC: 26.1.2)").when(server).getVersion();
doReturn("26.1.2.build.2570-experimental").when(server).getBukkitVersion();
MinecraftVersion version = MinecraftVersion.detect(server);
assertEquals("1.21.11", version.value());
assertEquals(21, version.major());
assertEquals(11, version.minor());
assertEquals("26.1.2", version.value());
assertEquals(26, version.major());
assertEquals(1, version.minor());
assertEquals(2, version.patch());
}
@Test
public void prefersRuntimeMinecraftVersionMethodWhenPresent() {
PaperLikeServer server = mock(PaperLikeServer.class);
doReturn("1.21.11").when(server).getMinecraftVersion();
doReturn("26.1.2").when(server).getMinecraftVersion();
doReturn("26.1.2-2570-e64b1b2 (MC: 26.1.2)").when(server).getVersion();
doReturn("26.1.2.build.2570-experimental").when(server).getBukkitVersion();
MinecraftVersion version = MinecraftVersion.detect(server);
assertEquals("1.21.11", version.value());
assertEquals(21, version.major());
assertEquals(11, version.minor());
assertEquals("26.1.2", version.value());
assertEquals(26, version.major());
assertEquals(1, version.minor());
assertEquals(2, version.patch());
}
@Test
@@ -48,16 +50,18 @@ public class MinecraftVersionTest {
@Test
public void parsesStandardBukkitSnapshotVersion() {
MinecraftVersion version = MinecraftVersion.fromBukkitVersion("1.21.11-R0.1-SNAPSHOT");
assertEquals("1.21.11", version.value());
assertEquals(21, version.major());
assertEquals(11, version.minor());
MinecraftVersion version = MinecraftVersion.fromBukkitVersion("26.1.2-R0.1-SNAPSHOT");
assertEquals("26.1.2", version.value());
assertEquals(26, version.major());
assertEquals(1, version.minor());
assertEquals(2, version.patch());
}
@Test
public void comparesMajorBeforeMinor() {
MinecraftVersion version = MinecraftVersion.fromBukkitVersion("1.20.12-R0.1-SNAPSHOT");
assertFalse(version.isAtLeast(21, 11));
assertTrue(version.isNewerThan(20, 11));
MinecraftVersion version = MinecraftVersion.fromBukkitVersion("26.1.2-R0.1-SNAPSHOT");
assertFalse(version.isAtLeast(26, 1, 3));
assertTrue(version.isAtLeast(26, 1, 2));
assertTrue(version.isSameRelease(26, 1, 2));
}
}
@@ -1,28 +1,28 @@
Paper 1.21.11
Paper 26.1.2
/iris create matrix-paper overworld
/iris std o overworld
close Studio world
benchmark create/unload
Purpur 1.21.11
Purpur 26.1.2
/iris create matrix-purpur overworld
/iris std o overworld
close Studio world
benchmark create/unload
Canvas 1.21.11
Canvas 26.1.2
/iris create matrix-canvas overworld
/iris std o overworld
close Studio world
benchmark create/unload
Folia 1.21.11
Folia 26.1.2
/iris create matrix-folia overworld
/iris std o overworld
close Studio world
benchmark create/unload
Spigot 1.21.11
Spigot 26.1.2
/iris create matrix-spigot overworld
/iris std o overworld
close Studio world
-1
View File
@@ -72,5 +72,4 @@ include(':core', ':core:agent')
include(':probe')
include(':spi')
include(':adapters:bukkit:plugin')
include(':adapters:bukkit:nms:v1_21_R7')
include(':adapters:bukkit:nms:v26_1_R1')