Limited borrowed types to modded

- Pack validation rejects borrowedDimensionType on platforms that generate their own Iris dimension type, with an error naming the supported loaders
- Dimension type keys and registry contracts honor borrowedDimensionType only when the platform generation registry supports borrowed types, so Bukkit always derives its generated iris type
- The Bukkit runtime contract always expects the generated Iris type; hotload still refuses a borrowedDimensionType change
This commit is contained in:
Brian Neumann-Fopiano committed 2026-10-14 21:10:25 -04:00
1 parent 9481a9ecf0
commit aedef3c38a
11 files changed
+133 -22

No files matched your search

@@ -139,16 +139,15 @@ public class IrisDimensionRuntimeContractTest {
}
@Test
public void expectedContractUsesTheBorrowedTypeKey() {
public void expectedContractIsTheGeneratedIrisTypeEvenWhenThePackBorrowsOne() {
IrisDimension dimension = borrowedDimension("aether:the_aether", 0, 256);
assertEquals("aether:the_aether", IrisDimensionRuntimeContract.expected(dimension, "iris").typeKey());
assertEquals("iris:skylands", IrisDimensionRuntimeContract.expected(dimension, "iris").typeKey());
}
@Test
public void borrowedTypeAcceptsAPackRangeInsideTheBorrowedRange() {
IrisDimensionRuntimeContract expected = IrisDimensionRuntimeContract.expected(
borrowedDimension("aether:the_aether", 0, 192), "iris");
IrisDimensionRuntimeContract expected = borrowedContract("aether:the_aether", 0, 192);
expected.requireBorrowedFit("Modded level 'aether:the_aether'",
new IrisDimensionRuntimeContract("aether:the_aether", 0, 256, 256));
@@ -158,8 +157,7 @@ public class IrisDimensionRuntimeContractTest {
@Test
public void borrowedTypeRejectsAPackTopAboveTheBorrowedTop() {
IrisDimensionRuntimeContract expected = IrisDimensionRuntimeContract.expected(
borrowedDimension("aether:the_aether", 0, 320), "iris");
IrisDimensionRuntimeContract expected = borrowedContract("aether:the_aether", 0, 320);
assertBorrowedFitRejected(expected, new IrisDimensionRuntimeContract("aether:the_aether", 0, 256, 256),
"0..320", "0..256");
@@ -167,8 +165,7 @@ public class IrisDimensionRuntimeContractTest {
@Test
public void borrowedTypeRejectsAPackMinimumThatDiffersFromTheBorrowedMinimum() {
IrisDimensionRuntimeContract expected = IrisDimensionRuntimeContract.expected(
borrowedDimension("aether:the_aether", 16, 256), "iris");
IrisDimensionRuntimeContract expected = borrowedContract("aether:the_aether", 16, 256);
assertBorrowedFitRejected(expected, new IrisDimensionRuntimeContract("aether:the_aether", 0, 256, 256),
"16..256", "0..256");
@@ -176,8 +173,7 @@ public class IrisDimensionRuntimeContractTest {
@Test
public void borrowedTypeRejectsALevelUsingAnotherType() {
IrisDimensionRuntimeContract expected = IrisDimensionRuntimeContract.expected(
borrowedDimension("aether:the_aether", 0, 256), "iris");
IrisDimensionRuntimeContract expected = borrowedContract("aether:the_aether", 0, 256);
assertBorrowedFitRejected(expected, new IrisDimensionRuntimeContract("minecraft:overworld", 0, 256, 256),
"aether:the_aether", "minecraft:overworld");
@@ -192,6 +188,10 @@ public class IrisDimensionRuntimeContractTest {
assertHotloadRejected(active, replacement, "aether:the_aether");
}
private static IrisDimensionRuntimeContract borrowedContract(String typeKey, int minHeight, int maxHeight) {
return new IrisDimensionRuntimeContract(typeKey, minHeight, maxHeight - minHeight, maxHeight - minHeight);
}
private IrisDimension borrowedDimension(String typeKey, int minHeight, int maxHeight) {
IrisDimension dimension = new IrisDimension();
dimension.setLoadKey("skylands");
@@ -46,9 +46,23 @@ public class PackValidatorBorrowedDimensionTypeTest {
validate("{\"borrowedDimensionType\":42}"));
}
@Test
public void rejectsBorrowedTypesOnPlatformsThatGenerateTheirOwnType() {
assertTrue(validate("{\"borrowedDimensionType\":\"\"}", false).isEmpty());
List<String> errors = validate("{\"borrowedDimensionType\":\"aether:the_aether\"}", false);
assertEquals(1, errors.size());
assertTrue(errors.getFirst(), errors.getFirst().contains("borrowedDimensionType"));
assertTrue(errors.getFirst(), errors.getFirst().contains("Fabric, Forge, and NeoForge"));
}
private static List<String> validate(String json) {
return validate(json, true);
}
private static List<String> validate(String json, boolean borrowingSupported) {
List<String> errors = new ArrayList<>();
PackDimensionValidator.validateBorrowedDimensionType("main", new JSONObject(json), errors);
PackDimensionValidator.validateBorrowedDimensionType("main", new JSONObject(json), borrowingSupported, errors);
return errors;
}
}
@@ -167,7 +167,7 @@ public class GenerationRegistryContractFactoryTest {
PlatformRegistries registries = mock(PlatformRegistries.class);
when(registries.blockTypeKeys()).thenReturn(new KList<>());
when(registries.entityKeys()).thenReturn(new KList<>());
FakeGenerationRegistry generationRegistry = new FakeGenerationRegistry("test-runtime");
FakeGenerationRegistry generationRegistry = new BorrowingGenerationRegistry("test-runtime");
GenerationRegistryContract contract = GenerationRegistryContractFactory.create(
data,
@@ -187,6 +187,38 @@ public class GenerationRegistryContractFactoryTest {
assertEquals("aether:the_aether", dimension.dimensionTypeResourceKey(generationRegistry, "borrowed-pack"));
}
@Test
public void platformWithoutBorrowedTypesKeepsTheGeneratedDimensionType() throws Exception {
File pack = temporary.newFolder("borrowed-pack-generated");
IrisData data = mock(IrisData.class);
when(data.getDataFolder()).thenReturn(pack);
when(data.getContentGate()).thenReturn(mock(ContentGate.class));
IrisDimension dimension = spy(new IrisDimension().setBorrowedDimensionType("aether:the_aether"));
dimension.setLoadKey("skylands");
stubDimensions(data, dimension);
doReturn(new KList<>()).when(dimension).getReachableBiomes(any());
doReturn(new KList<>()).when(dimension).getAllRegions(any());
PlatformRegistries registries = mock(PlatformRegistries.class);
when(registries.blockTypeKeys()).thenReturn(new KList<>());
when(registries.entityKeys()).thenReturn(new KList<>());
FakeGenerationRegistry generationRegistry = new FakeGenerationRegistry("test-runtime");
GenerationRegistryContract contract = GenerationRegistryContractFactory.create(
data,
dimension,
GenerationPackFingerprint.compute(pack.toPath(), GenerationPackFingerprint.CURRENT_VERSION),
DataVersion.getLatest().get(),
registries,
generationRegistry
);
assertFalse(contract.definitions().containsKey(key(
GenerationRegistryContractFactory.DIMENSION_TYPE_REGISTRY, "aether:the_aether")));
assertTrue(contract.definitions().containsKey(key(
GenerationRegistryContractFactory.DIMENSION_TYPE_REGISTRY, "test:skylands_type")));
assertEquals("test:skylands_type", dimension.dimensionTypeResourceKey(generationRegistry, "borrowed-pack-generated"));
}
@Test
public void duplicateLogicalBiomeIdsKeepExactDefinitionsAndChooseStableLegacyAlias() throws Exception {
File pack = temporary.newFolder("duplicate-biomes");
@@ -956,6 +988,17 @@ public class GenerationRegistryContractFactoryTest {
}
}
private static final class BorrowingGenerationRegistry extends FakeGenerationRegistry {
private BorrowingGenerationRegistry(String runtimeIdentity) {
super(runtimeIdentity);
}
@Override
public boolean supportsBorrowedDimensionTypes() {
return true;
}
}
private static class FakeGenerationRegistry implements PlatformGenerationRegistry {
private final String runtimeIdentity;
private final Map<GenerationRegistryContract.PhysicalResourceKey, Definition> definitions =