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

@@ -1112,8 +1112,12 @@ public class IrisDimension extends IrisRegistrant {
return borrowedDimensionType != null && !borrowedDimensionType.isBlank();
}
public boolean borrowsDimensionType(PlatformGenerationRegistry registry) {
return hasBorrowedDimensionType() && registry.supportsBorrowedDimensionTypes();
}
public String dimensionTypeResourceKey(PlatformGenerationRegistry registry, String packName) {
if (hasBorrowedDimensionType()) {
if (borrowsDimensionType(registry)) {
return borrowedDimensionType.trim();
}
return registry.dimensionTypeResourceKey(packName, getLoadKey(), getDimensionTypeKey());
@@ -46,9 +46,7 @@ public record IrisDimensionRuntimeContract(
Objects.requireNonNull(dimension, "dimension");
Objects.requireNonNull(namespace, "namespace");
return new IrisDimensionRuntimeContract(
dimension.hasBorrowedDimensionType()
? dimension.getBorrowedDimensionType().trim()
: namespace + ":" + dimension.getDimensionTypeKey(),
namespace + ":" + dimension.getDimensionTypeKey(),
dimension.getMinHeight(),
dimension.getMaxHeight() - dimension.getMinHeight(),
dimension.getLogicalHeight());
@@ -63,6 +61,7 @@ public record IrisDimensionRuntimeContract(
IrisDimensionRuntimeContract actual = expected(active, namespace);
IrisDimensionRuntimeContract proposed = expected(replacement, namespace);
proposed.requireExact(runtimeName, actual);
requireBorrowedTypeUnchanged(runtimeName, active, replacement);
requireGeneratedTypeCompatible(runtimeName, active, replacement);
}
@@ -120,6 +119,25 @@ public record IrisDimensionRuntimeContract(
+ ". Generation was refused before any chunk writes. Restart after installing the exact Iris dimension type; terrain clipping is not allowed.");
}
private static void requireBorrowedTypeUnchanged(
String runtimeName,
IrisDimension active,
IrisDimension replacement
) {
String activeBorrowed = borrowedType(active);
String replacementBorrowed = borrowedType(replacement);
if (activeBorrowed.equals(replacementBorrowed)) {
return;
}
throw new IrisDimensionContractException(runtimeName + " cannot hotload a changed borrowedDimensionType. The active pack borrows '"
+ activeBorrowed + "' and the replacement borrows '" + replacementBorrowed
+ "'. Close and reopen Studio after changing borrowedDimensionType.");
}
private static String borrowedType(IrisDimension dimension) {
return dimension.hasBorrowedDimensionType() ? dimension.getBorrowedDimensionType().trim() : "";
}
private static void requireGeneratedTypeCompatible(
String runtimeName,
IrisDimension active,
@@ -19,6 +19,7 @@
package art.arcane.iris.pack;
import art.arcane.iris.generation.terrain.IrisDimensionType;
import art.arcane.iris.spi.IrisPlatforms;
import art.arcane.iris.structure.object.IrisObjectScale;
import art.arcane.iris.world.IrisWorldBoundary;
import art.arcane.volmlib.util.json.JSONArray;
@@ -60,7 +61,7 @@ final class PackDimensionValidator {
validateImportedStructurePolicy(dimensionKey, dimJson, blockingErrors, warnings);
validateDimensionHeights(packFolder, dimensionKey, dimJson, blockingErrors);
validateBorrowedDimensionType(dimensionKey, dimJson, blockingErrors);
validateBorrowedDimensionType(dimensionKey, dimJson, borrowedDimensionTypesSupported(), blockingErrors);
validateSamplingStep(dimensionKey, dimJson, "terrainSamplingStep", 1, blockingErrors);
validateSamplingStep(dimensionKey, dimJson, "caveDensitySamplingStep", 1, blockingErrors);
validateSamplingStep(dimensionKey, dimJson, "biomeBoundsSamplingStep", 4, blockingErrors);
@@ -128,16 +129,29 @@ final class PackDimensionValidator {
}
}
static void validateBorrowedDimensionType(String dimensionKey, JSONObject dimension, List<String> errors) {
static void validateBorrowedDimensionType(String dimensionKey, JSONObject dimension, boolean borrowingSupported,
List<String> errors) {
if (!dimension.has(BORROWED_DIMENSION_TYPE) || dimension.isNull(BORROWED_DIMENSION_TYPE)) {
return;
}
Object value = dimension.opt(BORROWED_DIMENSION_TYPE);
if (value instanceof String key && (key.isEmpty() || RESOURCE_KEY.matcher(key).matches())) {
if (value instanceof String key && key.isEmpty()) {
return;
}
errors.add("Dimension '" + dimensionKey + "' " + BORROWED_DIMENSION_TYPE
+ " must be a namespaced dimension type key such as aether:the_aether, but was '" + value + "'.");
if (!(value instanceof String key) || !RESOURCE_KEY.matcher(key).matches()) {
errors.add("Dimension '" + dimensionKey + "' " + BORROWED_DIMENSION_TYPE
+ " must be a namespaced dimension type key such as aether:the_aether, but was '" + value + "'.");
return;
}
if (!borrowingSupported) {
errors.add("Dimension '" + dimensionKey + "' sets " + BORROWED_DIMENSION_TYPE + " to '" + key
+ "', which only Fabric, Forge, and NeoForge servers support. This server generates its own Iris dimension type, so remove "
+ BORROWED_DIMENSION_TYPE + " to use this pack here.");
}
}
private static boolean borrowedDimensionTypesSupported() {
return !IrisPlatforms.isBound() || IrisPlatforms.get().supportsBorrowedDimensionTypes();
}
static void validateObjectScaleFactor(String dimensionKey, JSONObject dimension, List<String> errors) {
@@ -175,7 +175,7 @@ public final class GenerationRegistryContractFactory {
List<IrisBiome> reachableBiomes = new ArrayList<>(requiredDimension.getReachableBiomes(() -> requiredData));
for (IrisDimension registryDimension : registryDimensions(requiredData, requiredDimension)) {
String dimensionKey = requireText(registryDimension.getLoadKey(), "Dimension load key");
if (!registryDimension.hasBorrowedDimensionType()) {
if (!registryDimension.borrowsDimensionType(requiredGenerationRegistry)) {
addGeneratedDimensionType(
definitions,
generatedSources,