mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-08-27 04:37:47 +00:00
First Test MV
This commit is contained in:
@@ -18,9 +18,9 @@ Canonical English is defined in the typed Java catalogs under `core/src/main/jav
|
||||
|
||||
| Platform | Artifact | Minecraft | Notes |
|
||||
|---|---|---|---|
|
||||
| Paper / Purpur / Leaf / Canvas | plugin jar | 26.2 | Full feature set |
|
||||
| Folia | plugin jar | 26.2 | Region-safe scheduling throughout |
|
||||
| Spigot / CraftBukkit | plugin jar | 26.2 | Full feature set |
|
||||
| Paper / Purpur / Leaf / Canvas | plugin jar | 26.1.2 - 26.2 | Full feature set |
|
||||
| Folia | plugin jar | 26.1.2 - 26.2 | Region-safe scheduling throughout |
|
||||
| Spigot / CraftBukkit | plugin jar | 26.1.2 - 26.2 | Full feature set |
|
||||
| Fabric | mod jar | 26.2 | Server worldgen + client HUD; requires Fabric Loader 0.19.3+ |
|
||||
| Forge | mod jar | 26.2 | Server worldgen + client HUD; requires Forge 65.0.4+ |
|
||||
| NeoForge | mod jar | 26.2 | Server worldgen + client HUD; requires NeoForge 26.2.0.12-beta+ |
|
||||
|
||||
+21
-17
@@ -6,6 +6,7 @@ import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder;
|
||||
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.MinecraftVersion;
|
||||
import art.arcane.iris.core.nms.container.BiomeColor;
|
||||
import art.arcane.iris.core.nms.container.Pair;
|
||||
import art.arcane.iris.core.nms.container.BlockProperty;
|
||||
@@ -161,6 +162,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class NMSBinding implements INMSBinding {
|
||||
private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
|
||||
private volatile DataVersion dataVersion;
|
||||
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();
|
||||
@@ -1238,23 +1240,19 @@ public class NMSBinding implements INMSBinding {
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
// Registry lookup instead of an EntityType static-field scan: 26.2 moved the constants
|
||||
// to a separate EntityTypes holder class that 26.1.2 does not have, while the registry
|
||||
// resolves identically on both. ENTITY_TYPE is a DefaultedRegistry, so guard containsKey
|
||||
// to avoid silently resolving unknown keys to the default entry.
|
||||
Identifier key = Identifier.fromNamespaceAndPath(entity.getKey().getNamespace(), entity.getKey().getKey());
|
||||
if (!BuiltInRegistries.ENTITY_TYPE.containsKey(key)) {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
EntityType<?> entityType = BuiltInRegistries.ENTITY_TYPE.getValue(key);
|
||||
if (entityType == null) {
|
||||
return null;
|
||||
}
|
||||
return new Vector3d(entityType.getWidth(), entityType.getHeight(), entityType.getWidth());
|
||||
} catch (Throwable e) {
|
||||
IrisLogging.error("Unable to get entity dimensions for " + entity + "!");
|
||||
IrisLogging.reportError(e);
|
||||
@@ -1362,7 +1360,13 @@ public class NMSBinding implements INMSBinding {
|
||||
|
||||
@Override
|
||||
public DataVersion getDataVersion() {
|
||||
return DataVersion.V26_2;
|
||||
DataVersion cached = dataVersion;
|
||||
if (cached == null) {
|
||||
MinecraftVersion detected = MinecraftVersion.detect(Bukkit.getServer());
|
||||
cached = detected != null && detected.isSameRelease(26, 1, 2) ? DataVersion.V26_1_2 : DataVersion.V26_2;
|
||||
dataVersion = cached;
|
||||
}
|
||||
return cached;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+16
-34
@@ -6,7 +6,6 @@ import art.arcane.iris.engine.object.IrisStructureTerrain;
|
||||
import art.arcane.iris.engine.object.IrisStructureTerrainMode;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Holder;
|
||||
@@ -17,7 +16,6 @@ import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.server.Bootstrap;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.LevelReader;
|
||||
import net.minecraft.world.level.LevelHeightAccessor;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
@@ -46,8 +44,11 @@ import net.minecraft.world.level.levelgen.structure.pools.ListPoolElement;
|
||||
import net.minecraft.world.level.levelgen.structure.pools.SinglePoolElement;
|
||||
import net.minecraft.world.level.levelgen.structure.pools.StructurePoolElement;
|
||||
import net.minecraft.world.level.levelgen.structure.pools.StructureTemplatePool;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.AlwaysTrueTest;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.BlockStateMatchTest;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.LiquidSettings;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.BlockIgnoreProcessor;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.ProcessorRule;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.RuleProcessor;
|
||||
import net.minecraft.world.level.levelgen.structure.structures.DesertPyramidPiece;
|
||||
import net.minecraft.world.level.levelgen.structure.structures.DesertPyramidStructure;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings;
|
||||
@@ -267,7 +268,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest {
|
||||
block(0, 0, 0, Blocks.COBBLESTONE.defaultBlockState()),
|
||||
block(15, 0, 0, Blocks.STONE.defaultBlockState())));
|
||||
InlineSinglePoolElement element = new InlineSinglePoolElement(
|
||||
sparseTemplate, List.of(new ReplaceBlockProcessor(
|
||||
sparseTemplate, List.of(replaceBlockProcessor(
|
||||
Blocks.STONE.defaultBlockState(), Blocks.AIR.defaultBlockState())));
|
||||
PoolElementStructurePiece piece = rigidTemplatePiece(
|
||||
element, new BoundingBox(0, 64, 0, 15, 70, 3), 1, Rotation.NONE);
|
||||
@@ -369,7 +370,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest {
|
||||
block(2, 0, 0, Blocks.AIR.defaultBlockState()),
|
||||
block(2, 2, 0, Blocks.COBBLESTONE.defaultBlockState())));
|
||||
InlineSinglePoolElement element = new InlineSinglePoolElement(
|
||||
template, List.of(new ReplaceBlockProcessor(
|
||||
template, List.of(replaceBlockProcessor(
|
||||
Blocks.STONE.defaultBlockState(), Blocks.AIR.defaultBlockState())));
|
||||
PoolElementStructurePiece piece = rigidTemplatePiece(
|
||||
element, new BoundingBox(0, 65, 0, 2, 72, 0), 1, Rotation.NONE);
|
||||
@@ -551,7 +552,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest {
|
||||
block(0, 0, 0, Blocks.STONE.defaultBlockState()),
|
||||
block(0, 2, 0, Blocks.COBBLESTONE.defaultBlockState())));
|
||||
PoolElementStructurePiece piece = rigidTemplatePiece(
|
||||
new InlineSinglePoolElement(template, List.of(new ReplaceBlockProcessor(
|
||||
new InlineSinglePoolElement(template, List.of(replaceBlockProcessor(
|
||||
Blocks.STONE.defaultBlockState(), Blocks.AIR.defaultBlockState()))),
|
||||
new BoundingBox(0, 66, 0, 7, 72, 7), 1, Rotation.NONE);
|
||||
StructureStart start = rigidSurfaceStart(List.of(piece), TerrainAdjustment.NONE);
|
||||
@@ -1081,10 +1082,10 @@ public class NativeStructurePostProcessorSurfaceTerrainTest {
|
||||
block(1, 0, 0, Blocks.STONE.defaultBlockState())));
|
||||
InlineSinglePoolElement upperElement = new InlineSinglePoolElement(
|
||||
upperTemplate, List.of(
|
||||
new ReplaceBlockProcessor(
|
||||
replaceBlockProcessor(
|
||||
Blocks.COBBLESTONE.defaultBlockState(),
|
||||
Blocks.AIR.defaultBlockState()),
|
||||
new ReplaceBlockProcessor(
|
||||
replaceBlockProcessor(
|
||||
Blocks.STONE.defaultBlockState(),
|
||||
Blocks.WATER.defaultBlockState())));
|
||||
PoolElementStructurePiece lower = rigidTemplatePiece(
|
||||
@@ -2131,32 +2132,13 @@ public class NativeStructurePostProcessorSurfaceTerrainTest {
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ReplaceBlockProcessor implements StructureProcessor {
|
||||
private final BlockState source;
|
||||
private final BlockState replacement;
|
||||
|
||||
private ReplaceBlockProcessor(BlockState source, BlockState replacement) {
|
||||
this.source = source;
|
||||
this.replacement = replacement;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StructureTemplate.StructureBlockInfo processBlock(
|
||||
LevelReader level, BlockPos targetPosition, BlockPos referencePos,
|
||||
BlockPos templateRelativePos,
|
||||
StructureTemplate.StructureBlockInfo processedBlockInfo,
|
||||
StructurePlaceSettings settings) {
|
||||
if (!processedBlockInfo.state().equals(source)) {
|
||||
return processedBlockInfo;
|
||||
}
|
||||
return new StructureTemplate.StructureBlockInfo(
|
||||
processedBlockInfo.pos(), replacement, processedBlockInfo.nbt());
|
||||
}
|
||||
|
||||
@Override
|
||||
public MapCodec<? extends StructureProcessor> codec() {
|
||||
return BlockIgnoreProcessor.STRUCTURE_BLOCK.codec();
|
||||
}
|
||||
private static StructureProcessor replaceBlockProcessor(BlockState source, BlockState replacement) {
|
||||
// Vanilla RuleProcessor instead of a custom StructureProcessor subtype: StructureProcessor
|
||||
// is an abstract class on 26.1.2 and an interface on 26.2, so a direct subtype cannot
|
||||
// compile against both dev bundles. BlockStateMatchTest matches the exact source state,
|
||||
// matching the previous custom processor's identity-equality semantics.
|
||||
return new RuleProcessor(List.of(new ProcessorRule(
|
||||
new BlockStateMatchTest(source), AlwaysTrueTest.INSTANCE, replacement)));
|
||||
}
|
||||
|
||||
private static StructureStart desertStart() {
|
||||
|
||||
+46
-43
@@ -1,15 +1,16 @@
|
||||
package art.arcane.iris.nativegen;
|
||||
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.HolderSet;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import net.minecraft.server.Bootstrap;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.util.valueproviders.ConstantInt;
|
||||
import net.minecraft.world.level.LevelReader;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.Rotation;
|
||||
@@ -34,11 +35,13 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProc
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessorList;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||
import org.junit.Assume;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -62,38 +65,33 @@ public class NativeStructureTemplateOccupancyTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void straddlingGravityPieceReadsHeightsOnlyInsideTheProcessingArea() throws Exception {
|
||||
public void straddlingGravityPieceHeightReadsHonorTheVanillaClipContract() throws Exception {
|
||||
ColumnRecorder recorder = new ColumnRecorder();
|
||||
|
||||
resolve(straddlingPiece(
|
||||
List.of(new GravityProcessor(Heightmap.Types.WORLD_SURFACE_WG, 0))), recorder);
|
||||
|
||||
assertEquals(columns(0, 15), recorder.heightColumns);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void straddlingPieceRunsProcessorsOnlyInsideTheProcessingArea() throws Exception {
|
||||
CountingProcessor counter = new CountingProcessor();
|
||||
|
||||
resolve(straddlingPiece(List.of(counter)), new ColumnRecorder());
|
||||
|
||||
assertEquals(columns(0, 15), counter.columns);
|
||||
assertTrue(counter.columns.size() < TEMPLATE_WIDTH);
|
||||
// 26.2 vanilla clips processor evaluation to the placement bounding box; 26.1.2 vanilla
|
||||
// runs processors across the whole piece. Iris mirrors the pinned version's semantics.
|
||||
assertEquals(expectedClippedColumns(), recorder.heightColumns);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cappedStraddlingPieceRunsProcessorsAcrossTheWholePiece() throws Exception {
|
||||
CountingProcessor counter = new CountingProcessor();
|
||||
ColumnRecorder recorder = new ColumnRecorder();
|
||||
|
||||
resolve(straddlingPiece(List.of(counter,
|
||||
new CappedProcessor(NopProcessor.INSTANCE, ConstantInt.of(0)))),
|
||||
new ColumnRecorder());
|
||||
resolve(straddlingPiece(List.of(
|
||||
new GravityProcessor(Heightmap.Types.WORLD_SURFACE_WG, 0),
|
||||
new CappedProcessor(NopProcessor.INSTANCE, ConstantInt.of(0)))), recorder);
|
||||
|
||||
assertEquals(columns(0, TEMPLATE_WIDTH - 1), counter.columns);
|
||||
assertEquals(columns(0, TEMPLATE_WIDTH - 1), recorder.heightColumns);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cappedProcessorIsTheOnlyProcessorThatDisablesTheProcessingAreaClip() {
|
||||
public void cappedProcessorIsTheOnlyProcessorThatDisablesTheProcessingAreaClip() throws Exception {
|
||||
Method contract = clipContractMethod();
|
||||
Assume.assumeTrue("evaluatesEntirePieceState only exists on 26.2+", contract != null);
|
||||
|
||||
for (StructureProcessor processor : List.of(
|
||||
BlockIgnoreProcessor.STRUCTURE_BLOCK,
|
||||
JigsawReplacementProcessor.INSTANCE,
|
||||
@@ -103,12 +101,12 @@ public class NativeStructureTemplateOccupancyTest {
|
||||
new GravityProcessor(Heightmap.Types.WORLD_SURFACE_WG, 0),
|
||||
new RuleProcessor(List.of()),
|
||||
new BlockAgeProcessor(0.5F),
|
||||
new ProtectedBlockProcessor(HolderSet.empty()))) {
|
||||
protectedBlockProcessor())) {
|
||||
assertFalse(processor.getClass().getName(),
|
||||
processor.evaluatesEntirePieceState());
|
||||
(Boolean) contract.invoke(processor));
|
||||
}
|
||||
assertTrue(new CappedProcessor(NopProcessor.INSTANCE, ConstantInt.of(4))
|
||||
.evaluatesEntirePieceState());
|
||||
assertTrue((Boolean) contract.invoke(
|
||||
new CappedProcessor(NopProcessor.INSTANCE, ConstantInt.of(4))));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -146,6 +144,30 @@ public class NativeStructureTemplateOccupancyTest {
|
||||
return columns;
|
||||
}
|
||||
|
||||
private static Method clipContractMethod() {
|
||||
try {
|
||||
return StructureProcessor.class.getMethod("evaluatesEntirePieceState");
|
||||
} catch (NoSuchMethodException absent) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static Set<Integer> expectedClippedColumns() {
|
||||
return clipContractMethod() != null
|
||||
? columns(PROCESSING_AREA.minX(), PROCESSING_AREA.maxX())
|
||||
: columns(0, TEMPLATE_WIDTH - 1);
|
||||
}
|
||||
|
||||
private static StructureProcessor protectedBlockProcessor() throws Exception {
|
||||
// Constructed reflectively: the constructor takes HolderSet on 26.2 and TagKey on 26.1.2.
|
||||
Constructor<?> constructor = ProtectedBlockProcessor.class.getConstructors()[0];
|
||||
Class<?> parameter = constructor.getParameterTypes()[0];
|
||||
Object argument = parameter == HolderSet.class
|
||||
? HolderSet.empty()
|
||||
: TagKey.create(Registries.BLOCK, Identifier.fromNamespaceAndPath("minecraft", "air"));
|
||||
return (StructureProcessor) constructor.newInstance(argument);
|
||||
}
|
||||
|
||||
private static StructureTemplateManager forbiddenTemplateManager() {
|
||||
throw new AssertionError("Inline templates must not resolve a template manager");
|
||||
}
|
||||
@@ -202,25 +224,6 @@ public class NativeStructureTemplateOccupancyTest {
|
||||
private final Set<Integer> stateColumns = new TreeSet<>();
|
||||
}
|
||||
|
||||
private static final class CountingProcessor implements StructureProcessor {
|
||||
private final Set<Integer> columns = new TreeSet<>();
|
||||
|
||||
@Override
|
||||
public StructureTemplate.StructureBlockInfo processBlock(
|
||||
LevelReader level, BlockPos targetPosition, BlockPos referencePos,
|
||||
BlockPos templateRelativePos,
|
||||
StructureTemplate.StructureBlockInfo processedBlockInfo,
|
||||
StructurePlaceSettings settings) {
|
||||
columns.add(processedBlockInfo.pos().getX());
|
||||
return processedBlockInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MapCodec<? extends StructureProcessor> codec() {
|
||||
return BlockIgnoreProcessor.STRUCTURE_BLOCK.codec();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class InlineSinglePoolElement extends SinglePoolElement {
|
||||
private InlineSinglePoolElement(
|
||||
StructureTemplate template, List<StructureProcessor> processors) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
String apiVersion = providers.gradleProperty('minecraftVersion').get()
|
||||
String apiVersion = providers.gradleProperty('apiVersion').get()
|
||||
def mainClass = 'art.arcane.iris.Iris'
|
||||
def bootstrapperClass = 'art.arcane.iris.IrisBootstrap'
|
||||
|
||||
|
||||
+1
-1
@@ -87,7 +87,7 @@ String forgeConsumerPath = "${consumerLocation}/forge-mod-consumers/dropins/mods
|
||||
String neoForgeConsumerPath = "${consumerLocation}/neoforge-mod-consumers/dropins/mods"
|
||||
|
||||
def nmsBindings = [
|
||||
v26_2_R1: '26.2.build.60-beta',
|
||||
v26_2_R1: '26.1.2.build.74-stable',
|
||||
]
|
||||
Class nmsTypeClass = Class.forName('NMSBinding$Type')
|
||||
nmsBindings.each { key, value ->
|
||||
|
||||
@@ -46,7 +46,6 @@ public final class IrisDatapackCompiler {
|
||||
List<File> packRoots,
|
||||
KList<File> datapackRoots,
|
||||
IDataFixer fixer,
|
||||
int packFormat,
|
||||
boolean adjustVanillaHeight
|
||||
) throws IOException {
|
||||
Objects.requireNonNull(packRoots, "packRoots");
|
||||
@@ -98,7 +97,7 @@ public final class IrisDatapackCompiler {
|
||||
}
|
||||
}
|
||||
|
||||
IrisDimension.writeShared(datapackRoots, height, packFormat, adjustVanillaHeight);
|
||||
IrisDimension.writeShared(datapackRoots, height, adjustVanillaHeight);
|
||||
validateOutputs(datapackRoots, dimensionCount);
|
||||
return new CompilationResult(packCount, dimensionCount, countBiomes(biomes));
|
||||
}
|
||||
|
||||
@@ -200,7 +200,6 @@ public class ServerConfigurator {
|
||||
packRoots,
|
||||
stagedRoots,
|
||||
fixer,
|
||||
BukkitPlatform.dataPackFormat(),
|
||||
IrisSettings.get().getGeneral().adjustVanillaHeight
|
||||
);
|
||||
for (int i = 0; i < liveRoots.size(); i++) {
|
||||
|
||||
@@ -257,7 +257,7 @@ public interface INMSBinding {
|
||||
if (!supportsIrisWorldGeneration()) {
|
||||
throw new IllegalStateException("Iris world '" + c.name() + "' cannot be created with limited NMS binding "
|
||||
+ getClass().getSimpleName()
|
||||
+ "; set general.disableNMS=false and use the supported Minecraft 26.2 server runtime");
|
||||
+ "; set general.disableNMS=false and use a supported Minecraft server runtime (26.1.2 or 26.2)");
|
||||
}
|
||||
if (missingDimensionTypes(generator.getTarget().getDimension().getDimensionTypeKey())) {
|
||||
throw new IllegalStateException("Missing dimension types to create world");
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package art.arcane.iris.core.nms;
|
||||
|
||||
final class NmsBindingSelector {
|
||||
private static final String SUPPORTED_VERSION = "26.2";
|
||||
private static final String SUPPORTED_VERSIONS = "26.1.2 or 26.2";
|
||||
private static final String SUPPORTED_TAG = "v26_2_R1";
|
||||
|
||||
private NmsBindingSelector() {
|
||||
@@ -11,8 +11,8 @@ final class NmsBindingSelector {
|
||||
if (version == null) {
|
||||
throw new IllegalStateException("Iris requires an exact Minecraft version before selecting NMS");
|
||||
}
|
||||
if (!version.isSameRelease(26, 2, 0)) {
|
||||
throw new IllegalStateException("Iris requires Minecraft " + SUPPORTED_VERSION
|
||||
if (!version.isSameRelease(26, 2, 0) && !version.isSameRelease(26, 1, 2)) {
|
||||
throw new IllegalStateException("Iris requires Minecraft " + SUPPORTED_VERSIONS
|
||||
+ ". Detected server version: " + version.value());
|
||||
}
|
||||
return SUPPORTED_TAG;
|
||||
|
||||
@@ -13,6 +13,7 @@ import java.util.function.Supplier;
|
||||
@Getter
|
||||
public enum DataVersion {
|
||||
UNSUPPORTED("0.0.0", 0, () -> null),
|
||||
V26_1_2("26.1.2", 101, DataFixerV1217::new),
|
||||
V26_2("26.2", 107, DataFixerV1217::new);
|
||||
private static final KMap<DataVersion, IDataFixer> cache = new KMap<>();
|
||||
@Getter(AccessLevel.NONE)
|
||||
@@ -51,4 +52,15 @@ public enum DataVersion {
|
||||
public static DataVersion getLatest() {
|
||||
return values()[values().length - 1];
|
||||
}
|
||||
|
||||
public static int minSupportedPackFormat() {
|
||||
int minimum = Integer.MAX_VALUE;
|
||||
for (DataVersion version : values()) {
|
||||
if (version == UNSUPPORTED) {
|
||||
continue;
|
||||
}
|
||||
minimum = Math.min(minimum, version.packFormat);
|
||||
}
|
||||
return minimum;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -293,6 +293,6 @@ public class NMSBinding1X implements INMSBinding {
|
||||
private IllegalStateException unsupportedStructureHook(String operation) {
|
||||
return new IllegalStateException("Iris cannot " + operation + " with limited NMS binding "
|
||||
+ getClass().getSimpleName()
|
||||
+ "; set general.disableNMS=false and use the supported Minecraft 26.2 server runtime");
|
||||
+ "; set general.disableNMS=false and use a supported Minecraft server runtime (26.1.2 or 26.2)");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,6 @@ import java.util.zip.ZipInputStream;
|
||||
public final class DefaultPackBootstrapProvisioner {
|
||||
private static final URI DEFAULT_SOURCE = URI.create("https://github.com/IrisDimensions/overworld/releases/download/beta/overworld.zip");
|
||||
private static final String WORLD_DATAPACK_DIRECTORY = "iris";
|
||||
private static final int PACK_FORMAT = 107;
|
||||
private static final int MARKER_SCHEMA = 2;
|
||||
private static final int MAX_ARCHIVE_ENTRIES = 100_000;
|
||||
private static final long MAX_ARCHIVE_BYTES = 512L * 1024L * 1024L;
|
||||
@@ -189,7 +188,7 @@ public final class DefaultPackBootstrapProvisioner {
|
||||
if (fixer == null) {
|
||||
throw new IOException("Latest Iris datapack fixer is unavailable during bootstrap");
|
||||
}
|
||||
IrisDatapackCompiler.compile(packRoots, outputFolders, fixer, PACK_FORMAT, false);
|
||||
IrisDatapackCompiler.compile(packRoots, outputFolders, fixer, false);
|
||||
if (!isDatapackRoot(compileContainer)) {
|
||||
throw new IOException("Canonical Iris datapack compiler produced incomplete output at " + compileContainer);
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import art.arcane.iris.core.loader.IrisData;
|
||||
import art.arcane.iris.spi.IrisLogging;
|
||||
import art.arcane.iris.spi.PlatformBlockState;
|
||||
import art.arcane.iris.core.loader.IrisRegistrant;
|
||||
import art.arcane.iris.core.nms.datapack.DataVersion;
|
||||
import art.arcane.iris.core.nms.datapack.IDataFixer;
|
||||
import art.arcane.iris.core.nms.datapack.IDataFixer.Dimension;
|
||||
import art.arcane.iris.engine.data.cache.AtomicCache;
|
||||
@@ -763,7 +764,6 @@ public class IrisDimension extends IrisRegistrant {
|
||||
public static void writeShared(
|
||||
KList<File> datapackRoots,
|
||||
DimensionHeight height,
|
||||
int packFormat,
|
||||
boolean adjustVanillaHeight
|
||||
) throws IOException {
|
||||
IrisLogging.debug(" Installing Data Pack Vanilla Dimension Types");
|
||||
@@ -774,16 +774,22 @@ public class IrisDimension extends IrisRegistrant {
|
||||
write(datapackRoot, "the_end", jsonStrings[2], adjustVanillaHeight);
|
||||
}
|
||||
|
||||
// Ranged formats spanning every supported runtime (26.1.2 = 101, 26.2 = 107). The bootstrap
|
||||
// provisioner stages this datapack before the server (and INMS) exists, so the runtime
|
||||
// version cannot be resolved there; a range keeps the emitted bytes identical on both
|
||||
// install paths and both Paper versions parse ranged min_format/max_format.
|
||||
int minFormat = DataVersion.minSupportedPackFormat();
|
||||
int maxFormat = DataVersion.getLatest().getPackFormat();
|
||||
String raw = """
|
||||
{
|
||||
"pack": {
|
||||
"description": "Iris Data Pack. This pack contains all installed Iris Packs' resources.",
|
||||
"pack_format": {},
|
||||
"min_format": {},
|
||||
"max_format": {}
|
||||
"pack_format": %d,
|
||||
"min_format": %d,
|
||||
"max_format": %d
|
||||
}
|
||||
}
|
||||
""".replace("{}", Integer.toString(packFormat));
|
||||
""".formatted(maxFormat, minFormat, maxFormat);
|
||||
|
||||
for (File datapackRoot : datapackRoots) {
|
||||
File mcm = new File(datapackRoot, "pack.mcmeta");
|
||||
|
||||
@@ -242,10 +242,6 @@ public final class BukkitPlatform implements IrisPlatform {
|
||||
return INMS.get().applyCustomNbt(itemStack, customNbt);
|
||||
}
|
||||
|
||||
public static int dataPackFormat() {
|
||||
return INMS.get().getDataVersion().getPackFormat();
|
||||
}
|
||||
|
||||
public static java.util.concurrent.CompletableFuture<Boolean> teleportAsync(Entity entity, Location destination) {
|
||||
return io.papermc.lib.PaperLib.teleportAsync(entity, destination);
|
||||
}
|
||||
|
||||
@@ -36,6 +36,11 @@ public interface DirectorExecutor extends DirectorExecutorBase {
|
||||
return sender == null ? null : sender.player();
|
||||
}
|
||||
|
||||
default boolean playerWorldGeneratesStructures() {
|
||||
Player activePlayer = player();
|
||||
return activePlayer != null && activePlayer.getWorld().canGenerateStructures();
|
||||
}
|
||||
|
||||
default IrisData data() {
|
||||
var access = access();
|
||||
if (access != null) {
|
||||
|
||||
+1
-3
@@ -29,7 +29,6 @@ import art.arcane.iris.spi.PlatformStructureHooks;
|
||||
import art.arcane.iris.util.common.director.DirectorParameterHandler;
|
||||
import art.arcane.volmlib.util.collection.KList;
|
||||
import art.arcane.volmlib.util.director.exceptions.DirectorParsingException;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
@@ -100,8 +99,7 @@ public class StructureHandler implements DirectorParameterHandler<String> {
|
||||
}
|
||||
|
||||
protected boolean nativeStructureGenerationEnabled() {
|
||||
Player activePlayer = player();
|
||||
return activePlayer != null && activePlayer.getWorld().canGenerateStructures();
|
||||
return playerWorldGeneratesStructures();
|
||||
}
|
||||
|
||||
private static Map<String, String> distinctKeys(List<String> keys) {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package art.arcane.iris.core;
|
||||
|
||||
import art.arcane.iris.core.nms.datapack.DataVersion;
|
||||
import art.arcane.iris.core.nms.datapack.v1217.DataFixerV1217;
|
||||
import art.arcane.volmlib.util.collection.KList;
|
||||
import art.arcane.volmlib.util.json.JSONObject;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.Assume;
|
||||
@@ -41,7 +43,6 @@ public class IrisDatapackCompilerTest {
|
||||
packRoots,
|
||||
new KList<File>().qadd(datapackRoot.toFile()),
|
||||
new DataFixerV1217(),
|
||||
107,
|
||||
false
|
||||
);
|
||||
|
||||
@@ -72,7 +73,6 @@ public class IrisDatapackCompilerTest {
|
||||
List.of(packRoot.toFile()),
|
||||
new KList<File>().qadd(datapackRoot.toFile()),
|
||||
new DataFixerV1217(),
|
||||
107,
|
||||
false
|
||||
);
|
||||
|
||||
@@ -99,6 +99,33 @@ public class IrisDatapackCompilerTest {
|
||||
IrisDatapackCompiler.collectPackRoots(dataDirectory, serverRoot));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void packMcmetaSpansEverySupportedRuntimeFormat() throws Exception {
|
||||
Path datapackRoot = temporaryFolder.newFolder("mcmeta-datapack").toPath();
|
||||
|
||||
IrisDatapackCompiler.compile(
|
||||
List.of(),
|
||||
new KList<File>().qadd(datapackRoot.toFile()),
|
||||
new DataFixerV1217(),
|
||||
false
|
||||
);
|
||||
|
||||
JSONObject pack = new JSONObject(Files.readString(datapackRoot.resolve("pack.mcmeta"), StandardCharsets.UTF_8))
|
||||
.getJSONObject("pack");
|
||||
int minFormat = pack.getInt("min_format");
|
||||
int maxFormat = pack.getInt("max_format");
|
||||
|
||||
assertEquals(101, minFormat);
|
||||
assertEquals(107, maxFormat);
|
||||
assertEquals(maxFormat, pack.getInt("pack_format"));
|
||||
|
||||
// One artifact serves both runtimes: each supported DataVersion's format must be in range.
|
||||
assertTrue(minFormat <= DataVersion.V26_1_2.getPackFormat()
|
||||
&& DataVersion.V26_1_2.getPackFormat() <= maxFormat);
|
||||
assertTrue(minFormat <= DataVersion.V26_2.getPackFormat()
|
||||
&& DataVersion.V26_2.getPackFormat() <= maxFormat);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compilingNoPacksPublishesCleanEmptyDatapack() throws Exception {
|
||||
Path datapackRoot = temporaryFolder.newFolder("empty-datapack").toPath();
|
||||
@@ -110,7 +137,6 @@ public class IrisDatapackCompilerTest {
|
||||
List.of(),
|
||||
new KList<File>().qadd(datapackRoot.toFile()),
|
||||
new DataFixerV1217(),
|
||||
107,
|
||||
false
|
||||
);
|
||||
|
||||
|
||||
@@ -57,6 +57,30 @@ public class MinecraftVersionTest {
|
||||
assertEquals(0, version.patch());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parsesThreePartNewSchemeVersion() {
|
||||
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());
|
||||
assertTrue(version.isSameRelease(26, 1, 2));
|
||||
assertFalse(version.isSameRelease(26, 2, 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void detectsThreePartVersionFromDecoratedVersion() {
|
||||
Server server = mock(Server.class);
|
||||
doReturn("git-Paper-74 (MC: 26.1.2)").when(server).getVersion();
|
||||
doReturn("26.1.2-R0.1-SNAPSHOT").when(server).getBukkitVersion();
|
||||
|
||||
MinecraftVersion version = MinecraftVersion.detect(server);
|
||||
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("26.2-R0.1-SNAPSHOT");
|
||||
|
||||
@@ -14,6 +14,13 @@ public class NmsBindingSelectorTest {
|
||||
assertEquals("v26_2_R1", NmsBindingSelector.select(version));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void selectsSharedRevisionFor2612() {
|
||||
MinecraftVersion version = MinecraftVersion.fromBukkitVersion("26.1.2-R0.1-SNAPSHOT");
|
||||
|
||||
assertEquals("v26_2_R1", NmsBindingSelector.select(version));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rejectsUnsupportedVersionsWithoutProbingAnotherRevision() {
|
||||
MinecraftVersion version = MinecraftVersion.fromBukkitVersion("26.3-R0.1-SNAPSHOT");
|
||||
@@ -24,6 +31,16 @@ public class NmsBindingSelectorTest {
|
||||
assertTrue(failure.getMessage().contains("26.3"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rejectsBasePatchOfSupportedMinorLine() {
|
||||
MinecraftVersion version = MinecraftVersion.fromBukkitVersion("26.1-R0.1-SNAPSHOT");
|
||||
|
||||
IllegalStateException failure = assertThrows(IllegalStateException.class,
|
||||
() -> NmsBindingSelector.select(version));
|
||||
|
||||
assertTrue(failure.getMessage().contains("26.1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rejectsMissingVersionDetection() {
|
||||
assertThrows(IllegalStateException.class, () -> NmsBindingSelector.select(null));
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package art.arcane.iris.core.nms.datapack;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
|
||||
public class DataVersionTest {
|
||||
@Test
|
||||
public void latestRemainsNewestSupportedRelease() {
|
||||
assertSame(DataVersion.V26_2, DataVersion.getLatest());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void packFormatsMatchServerVersionJson() {
|
||||
assertEquals(101, DataVersion.V26_1_2.getPackFormat());
|
||||
assertEquals(107, DataVersion.V26_2.getPackFormat());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void minSupportedPackFormatIsTheOldestSupportedRuntime() {
|
||||
assertEquals(101, DataVersion.minSupportedPackFormat());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bothSupportedReleasesShareAFixer() {
|
||||
assertNotNull(DataVersion.V26_1_2.get());
|
||||
assertNotNull(DataVersion.V26_2.get());
|
||||
assertSame(DataVersion.V26_1_2.get().getClass(), DataVersion.V26_2.get().getClass());
|
||||
}
|
||||
}
|
||||
@@ -8,9 +8,13 @@ to a new Minecraft version, in order.
|
||||
|
||||
`gradle.properties`:
|
||||
|
||||
- `minecraftVersion` — target MC version (e.g. `26.2`). Drives the Bukkit plugin `api-version`,
|
||||
- `minecraftVersion` — target MC version (e.g. `26.2`). Drives
|
||||
`BuildConstants.MINECRAFT_VERSION`, the `com.mojang:minecraft` coordinate, all mod-metadata
|
||||
minecraft ranges, and every dist/jar artifact name.
|
||||
- `apiVersion` — Bukkit plugin `api-version` (e.g. `26.1`). Deliberately decoupled from
|
||||
`minecraftVersion`: it is the lowest Minecraft release line the single plugin artifact loads on
|
||||
(currently `26.1` so one jar serves 26.1.2 and 26.2). Bump it only when dropping support for the
|
||||
older line.
|
||||
- `fabricLoaderVersion` — Fabric Loader version.
|
||||
- `forgeVersion` — Forge version (`<mc>-<forge>`).
|
||||
- `neoForgeVersion` — NeoForge version.
|
||||
@@ -19,7 +23,8 @@ to a new Minecraft version, in order.
|
||||
## Ordered steps
|
||||
|
||||
1. Edit `gradle.properties`: update `minecraftVersion`, `fabricLoaderVersion`, `forgeVersion`,
|
||||
`neoForgeVersion`, and the `irisVersion` suffix.
|
||||
`neoForgeVersion`, and the `irisVersion` suffix. Revisit `apiVersion` only if the bump drops
|
||||
support for the oldest Minecraft line the plugin artifact still loads on.
|
||||
|
||||
2. Edit `gradle/libs.versions.toml`:
|
||||
- `spigot` — the Spigot/Paper API pin used to compile against (`<mc>-R0.1-SNAPSHOT`).
|
||||
@@ -148,7 +153,7 @@ to a new Minecraft version, in order.
|
||||
|
||||
## Derived automatically (do not hand-edit on a version bump)
|
||||
|
||||
- Bukkit plugin `api-version` — `adapters/bukkit/plugin/build.gradle` reads `minecraftVersion`.
|
||||
- Bukkit plugin `api-version` — `adapters/bukkit/plugin/build.gradle` reads `apiVersion`.
|
||||
- `BuildConstants.MINECRAFT_VERSION` — stamped by the `generateTemplates` task in
|
||||
`core/build.gradle` from `minecraftVersion`; consumed by `Tasks.supportedVersions`.
|
||||
- Mod-metadata `minecraft` version ranges — templated from `minecraftVersion` at `processResources`.
|
||||
|
||||
@@ -25,6 +25,8 @@ nmsTools.repo-url=https://repo.codemc.org/repository/nms/
|
||||
nmsTools.specialSourceVersion=1.11.4
|
||||
irisVersion=4.0.0-26.2
|
||||
minecraftVersion=26.2
|
||||
# Bukkit plugin api-version: lowest supported Minecraft release line so one artifact loads on 26.1.2 and 26.2
|
||||
apiVersion=26.1
|
||||
fabricLoaderVersion=0.19.3
|
||||
forgeVersion=26.2-65.0.4
|
||||
neoForgeVersion=26.2.0.12-beta
|
||||
|
||||
@@ -12,7 +12,7 @@ sentryPlugin = "6.14.0" # https://github.com/getsentry/sentry-android-gradle-plu
|
||||
# Core Libraries
|
||||
lombok = "1.18.46"
|
||||
spigot = "26.2-R0.1-SNAPSHOT" # https://hub.spigotmc.org/nexus/repository/snapshots/org/spigotmc/spigot-api/maven-metadata.xml
|
||||
paper-api = "26.2.build.60-beta"
|
||||
paper-api = "26.1.2.build.74-stable"
|
||||
log4j = "2.26.1" # https://central.sonatype.com/artifact/org.apache.logging.log4j/log4j-api
|
||||
adventure-api = "4.26.1" # https://github.com/KyoriPowered/adventure
|
||||
adventure-platform = "4.4.1" # https://github.com/KyoriPowered/adventure-platform
|
||||
|
||||
Reference in New Issue
Block a user