Test- 26.3

This commit is contained in:
Brian Neumann-Fopiano committed 2026-09-19 23:48:44 -04:00
1 parent edadd14f4b
commit f0e7fea13b
107 files changed
+25110 -86

No files matched your search

@@ -62,15 +62,10 @@ public final class DefaultPackBootstrapProvisioner {
return DEFAULT_PACKS;
}
public static ProvisionResult provision(Path dataDirectory, Consumer<String> feedback) throws IOException {
return provision(dataDirectory, feedback, BukkitStartupPaths.resolveCurrent());
}
public static ProvisionResult provision(
Path dataDirectory,
Consumer<String> feedback,
BukkitStartupPaths startupPaths
) throws IOException {
public static ProvisionResult provision(BootstrapRequest request) throws IOException {
Path dataDirectory = request.dataDirectory();
Consumer<String> feedback = request.feedback();
BukkitStartupPaths startupPaths = request.startupPaths();
Objects.requireNonNull(dataDirectory, "dataDirectory");
Objects.requireNonNull(feedback, "feedback");
BukkitStartupPaths requiredStartupPaths = Objects.requireNonNull(startupPaths, "startupPaths");
@@ -94,7 +89,8 @@ public final class DefaultPackBootstrapProvisioner {
3,
Duration.ofMillis(250),
MAX_ARCHIVE_BYTES,
levelRoot
levelRoot,
request.dataVersion()
);
return provision(dataDirectory, feedback, options);
}
@@ -135,7 +131,7 @@ public final class DefaultPackBootstrapProvisioner {
if (!Integer.toString(MARKER_SCHEMA).equals(marker.getProperty("schema"))) {
return false;
}
IDataFixer fixer = DataVersion.getLatest().get();
IDataFixer fixer = DataVersion.getRuntime().get();
if (fixer == null
|| !IrisDatapackCompiler.compilerIdentity(fixer)
.equals(marker.getProperty("compilerIdentity"))) {
@@ -248,9 +244,9 @@ public final class DefaultPackBootstrapProvisioner {
}
List<File> packRoots = IrisDatapackCompiler.collectPackRoots(normalizedData, options.levelRoot());
IDataFixer fixer = DataVersion.getLatest().get();
IDataFixer fixer = options.dataVersion().get();
if (fixer == null) {
throw new IOException("Latest Iris datapack fixer is unavailable during bootstrap");
throw new IOException("Selected Iris datapack fixer is unavailable during bootstrap");
}
String compilerIdentity = IrisDatapackCompiler.compilerIdentity(fixer);
String aggregateFingerprint = packRootsFingerprint(packRoots);
@@ -915,6 +911,16 @@ public final class DefaultPackBootstrapProvisioner {
}
}
public record BootstrapRequest(Path dataDirectory, Consumer<String> feedback,
BukkitStartupPaths startupPaths, DataVersion dataVersion) {
public BootstrapRequest {
Objects.requireNonNull(dataDirectory);
Objects.requireNonNull(feedback);
Objects.requireNonNull(startupPaths);
Objects.requireNonNull(dataVersion);
}
}
record ProvisionOptions(
List<PackSpec> packs,
List<IrisGeneratorBinding> bindings,
@@ -925,7 +931,8 @@ public final class DefaultPackBootstrapProvisioner {
int attempts,
Duration retryDelay,
long maxArchiveBytes,
Path levelRoot
Path levelRoot,
DataVersion dataVersion
) {
ProvisionOptions {
packs = List.copyOf(Objects.requireNonNull(packs, "packs"));
@@ -936,6 +943,10 @@ public final class DefaultPackBootstrapProvisioner {
Objects.requireNonNull(requestTimeout, "requestTimeout");
Objects.requireNonNull(retryDelay, "retryDelay");
Objects.requireNonNull(levelRoot, "levelRoot");
Objects.requireNonNull(dataVersion, "dataVersion");
if (dataVersion == DataVersion.UNSUPPORTED) {
throw new IllegalArgumentException("Unsupported bootstrap datapack version");
}
if (attempts < 1 || maxArchiveBytes < 1L) {
throw new IllegalArgumentException("Invalid bootstrap provisioning options");
}
@@ -321,7 +321,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 a supported Minecraft server runtime (26.1.2 or 26.2)");
+ "; set general.disableNMS=false and use a supported Minecraft server runtime (26.1.2, 26.2 or 26.3)");
}
if (missingDimensionTypes(generator.getTarget().getDimension().getDimensionTypeKey())) {
throw new IllegalStateException("Missing dimension types to create world");
@@ -1,7 +1,7 @@
package art.arcane.iris.platform.bukkit.nms;
final class NmsBindingSelector {
private static final String SUPPORTED_VERSIONS = "26.1.2 or 26.2";
private static final String SUPPORTED_VERSIONS = "26.1.2, 26.2 or 26.3";
private static final String SUPPORTED_TAG = "v26_2_R1";
private NmsBindingSelector() {
@@ -11,6 +11,9 @@ final class NmsBindingSelector {
if (version == null) {
throw new IllegalStateException("Iris requires an exact Minecraft version before selecting NMS");
}
if (version.isSameRelease(26, 3, 0)) {
return "v26_3_R1";
}
if (!version.isSameRelease(26, 2, 0) && !version.isSameRelease(26, 1, 2)) {
throw new IllegalStateException("Iris requires Minecraft " + SUPPORTED_VERSIONS
+ ". Detected server version: " + version.value());
@@ -1,6 +1,7 @@
package art.arcane.iris.platform.bukkit.nms.datapack;
import art.arcane.iris.spi.IrisLogging;
import art.arcane.iris.spi.IrisPlatforms;
import art.arcane.iris.platform.bukkit.nms.INMS;
import art.arcane.iris.platform.bukkit.nms.datapack.v1217.DataFixerV1217;
import art.arcane.volmlib.util.collection.KMap;
@@ -8,13 +9,15 @@ import lombok.AccessLevel;
import lombok.Getter;
import java.util.function.Supplier;
import art.arcane.iris.platform.bukkit.nms.datapack.v263.DataFixerV263;
//https://minecraft.wiki/w/Pack_format
@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);
V26_2("26.2", 107, DataFixerV1217::new),
V26_3("26.3", 121, DataFixerV263::new);
private static final KMap<DataVersion, IDataFixer> cache = new KMap<>();
@Getter(AccessLevel.NONE)
private final Supplier<IDataFixer> constructor;
@@ -49,6 +52,29 @@ public enum DataVersion {
return fixer;
}
public static DataVersion forMinecraftVersion(String version) {
if (version == null) {
return UNSUPPORTED;
}
return switch (version) {
case "26.1.2" -> V26_1_2;
case "26.2", "26.2.0" -> V26_2;
case "26.3", "26.3.0" -> V26_3;
default -> UNSUPPORTED;
};
}
public static DataVersion getRuntime() {
if (!IrisPlatforms.isBound() || IrisPlatforms.get().minecraftVersion() == null) {
return getLatest();
}
DataVersion version = forMinecraftVersion(IrisPlatforms.get().minecraftVersion());
if (version == UNSUPPORTED) {
throw new IllegalStateException("Unsupported Minecraft datapack version: " + IrisPlatforms.get().minecraftVersion());
}
return version;
}
public static DataVersion getLatest() {
return values()[values().length - 1];
}
@@ -0,0 +1,54 @@
package art.arcane.iris.platform.bukkit.nms.datapack.v263;
import art.arcane.iris.generation.biome.IrisBiomeCustom;
import art.arcane.iris.platform.bukkit.nms.datapack.v1217.DataFixerV1217;
import art.arcane.volmlib.util.json.JSONArray;
import art.arcane.volmlib.util.json.JSONObject;
import java.util.Locale;
public final class DataFixerV263 extends DataFixerV1217 {
@Override
public JSONObject fixCustomBiome(IrisBiomeCustom biome, JSONObject json) {
JSONObject fixed = super.fixCustomBiome(biome, json);
JSONObject attributes = fixed.getJSONObject("attributes");
JSONObject effects = fixed.getJSONObject("effects");
for (String key : new String[]{"water_color", "foliage_color", "dry_foliage_color", "grass_color"}) {
color(effects, key);
}
for (String key : new String[]{"minecraft:visual/sky_color", "minecraft:visual/fog_color", "minecraft:visual/water_fog_color"}) {
color(attributes, key);
}
JSONObject spawners = fixed.getJSONObject("spawners");
for (String category : spawners.keySet()) {
JSONArray entries = spawners.getJSONArray(category);
for (int index = 0; index < entries.length(); index++) {
JSONObject spawn = entries.getJSONObject(index);
int minimum = spawn.getInt("minCount");
int maximum = spawn.getInt("maxCount");
spawn.remove("minCount");
spawn.remove("maxCount");
spawn.put("count", minimum == maximum ? minimum : new JSONObject()
.put("type", "minecraft:uniform")
.put("min_inclusive", minimum)
.put("max_inclusive", maximum));
}
}
attributes.put("minecraft:gameplay/natural_mob_spawns", new JSONObject()
.put("spawns_by_category", spawners)
.put("spawn_costs", fixed.getJSONObject("spawn_costs")));
Object probability = fixed.remove("creature_spawn_probability");
if (probability != null) {
attributes.put("minecraft:gameplay/creature_world_gen_spawn_probability", probability);
}
fixed.remove("spawners");
fixed.remove("spawn_costs");
return fixed;
}
private static void color(JSONObject object, String key) {
if (object.opt(key) instanceof Number value) {
object.put(key, String.format(Locale.ROOT, "#%06x", value.intValue() & 0xFFFFFF));
}
}
}
@@ -317,6 +317,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 a supported Minecraft server runtime (26.1.2 or 26.2)");
+ "; set general.disableNMS=false and use a supported Minecraft server runtime (26.1.2, 26.2 or 26.3)");
}
}
@@ -1,6 +1,7 @@
package art.arcane.iris.structure.export;
import art.arcane.volmlib.util.nbt.tag.CompoundTag;
import art.arcane.volmlib.util.nbt.mca.MCABlockStateCodecSupport;
import java.util.Collections;
import java.util.LinkedHashMap;
@@ -73,15 +74,15 @@ record VanillaBlockState(String name, Map<String, String> properties) {
return builder.append(']').toString();
}
CompoundTag toNbt() {
CompoundTag toNbt(MCABlockStateCodecSupport.Format format) {
CompoundTag state = new CompoundTag();
state.putString("Name", name);
state.putString(format.nameKey(), name);
if (!properties.isEmpty()) {
CompoundTag propertyTag = new CompoundTag();
for (Map.Entry<String, String> property : properties.entrySet()) {
propertyTag.putString(property.getKey(), property.getValue());
}
state.put("Properties", propertyTag);
state.put(format.propertiesKey(), propertyTag);
}
return state;
}
@@ -211,7 +211,7 @@ final class VanillaJigsawExportCompiler {
addError(diagnostics,
VanillaJigsawExportDiagnostic.Code.INVALID_MAX_DEPTH,
structureKey(structure),
"Minecraft 26.2 jigsaw size must be within 0..20; Iris export requires 1..20.");
"Minecraft jigsaw size must be within 0..20; Iris export requires 1..20.");
}
long horizontalDistance = (long) structure.getMaxSizeChunks() * 16L;
if (horizontalDistance < 1L || horizontalDistance > MAX_HORIZONTAL_DISTANCE) {
@@ -219,7 +219,7 @@ final class VanillaJigsawExportCompiler {
VanillaJigsawExportDiagnostic.Code.INVALID_MAX_DISTANCE,
structureKey(structure),
"Iris maxSizeChunks maps to " + horizontalDistance
+ " blocks, outside Minecraft 26.2's 1..128 horizontal limit.");
+ " blocks, outside Minecraft's 1..128 horizontal limit.");
}
int terrainPadding = settings.terrainAdaptation() == VanillaJigsawExportSettings.TerrainAdaptation.NONE
? 0 : 12;
@@ -315,7 +315,7 @@ final class VanillaJigsawExportCompiler {
addError(diagnostics,
VanillaJigsawExportDiagnostic.Code.INVALID_POOL_WEIGHT,
poolKey + "/pieces[" + index + "]",
"Minecraft 26.2 template-pool weights must be within 1..150.");
"Minecraft template-pool weights must be within 1..150.");
}
if (entry.getChance() != 1D) {
addError(diagnostics,
@@ -577,10 +577,12 @@ final class VanillaJigsawExportCompiler {
JsonObject pack = new JsonObject();
pack.addProperty("description", request.description());
JsonArray minimum = new JsonArray();
minimum.add(107);
minimum.add(1);
boolean current = VanillaStructureTemplateEncoder.targets263();
int packFormat = current ? 121 : 107;
minimum.add(packFormat);
minimum.add(current ? 0 : 1);
pack.add("min_format", minimum);
pack.addProperty("max_format", 107);
pack.addProperty("max_format", packFormat);
JsonObject root = new JsonObject();
root.add("pack", pack);
return root;
@@ -65,7 +65,7 @@ public final class VanillaJigsawExportRequest {
private final Path output;
private String namespace = "iris";
private String resourcePath;
private String description = "Iris vanilla jigsaw export for Minecraft 26.2";
private String description = "Iris vanilla jigsaw export";
private VanillaJigsawExportFormat format = VanillaJigsawExportFormat.DIRECTORY;
private VanillaJigsawExportSettings settings = VanillaJigsawExportSettings.defaults();
private boolean replaceExisting;
@@ -1,6 +1,8 @@
package art.arcane.iris.structure.export;
import art.arcane.iris.pack.value.IrisDirection;
import art.arcane.iris.spi.IrisPlatforms;
import art.arcane.volmlib.util.nbt.mca.MCABlockStateCodecSupport;
import art.arcane.iris.structure.jigsaw.IrisJigsawConnector;
import art.arcane.iris.structure.jigsaw.IrisJigsawPiece;
import art.arcane.iris.structure.object.IrisObject;
@@ -51,18 +53,26 @@ final class VanillaStructureTemplateEncoder {
}
}
boolean lowercase = targets263();
MCABlockStateCodecSupport.Format format = lowercase
? MCABlockStateCodecSupport.Format.LOWERCASE : MCABlockStateCodecSupport.Format.CAPITALIZED;
CompoundTag root = new CompoundTag();
root.put("size", intList(object.getW(), object.getH(), object.getD()));
root.put("palette", palette(paletteStates));
root.put("palette", palette(paletteStates, format));
root.put("blocks", blocks(orderedBlocks, paletteIndexes));
root.put("entities", new ListTag<>(CompoundTag.class));
root.putInt("DataVersion", DATA_VERSION_26_2);
root.putInt("DataVersion", lowercase ? 5023 : DATA_VERSION_26_2);
ByteArrayOutputStream output = new ByteArrayOutputStream();
NBTUtil.write(new NamedTag("", root), output, true);
return output.toByteArray();
}
static boolean targets263() {
String minecraftVersion = IrisPlatforms.get().minecraftVersion();
return "26.3".equals(minecraftVersion) || "26.3.0".equals(minecraftVersion);
}
private Map<BlockPosition, BlockEntry> objectBlocks(IrisObject object) {
Map<BlockPosition, BlockEntry> blocks = new LinkedHashMap<>();
Vector3i center = object.getCenter();
@@ -135,10 +145,10 @@ final class VanillaStructureTemplateEncoder {
};
}
private ListTag<CompoundTag> palette(List<VanillaBlockState> states) {
private ListTag<CompoundTag> palette(List<VanillaBlockState> states, MCABlockStateCodecSupport.Format format) {
ListTag<CompoundTag> palette = new ListTag<>(CompoundTag.class);
for (VanillaBlockState state : states) {
palette.add(state.toNbt());
palette.add(state.toNbt(format));
}
return palette;
}
@@ -110,7 +110,7 @@ public final class GenerationRegistryContractFactory {
data,
dimension,
packFingerprint,
DataVersion.getLatest().get(),
DataVersion.getRuntime().get(),
registries,
registries.generationRegistry(),
aliasPolicy
@@ -224,7 +224,7 @@ public final class GenerationRegistryContractFactory {
data,
dimension,
customBiome,
DataVersion.getLatest().get(),
DataVersion.getRuntime().get(),
registry
);
}
@@ -508,7 +508,7 @@ public final class GenerationRegistryContractFactory {
) throws IOException {
return captureRequiredDefinitions(
requiredContracts,
DataVersion.getLatest().get(),
DataVersion.getRuntime().get(),
IrisPlatforms.get().registries().generationRegistry()
);
}
@@ -519,7 +519,7 @@ public final class GenerationRegistryContractFactory {
) throws IOException {
return captureRequiredDefinitions(
requiredContracts,
DataVersion.getLatest().get(),
DataVersion.getRuntime().get(),
generationRegistry
);
}
@@ -603,7 +603,7 @@ public final class GenerationRegistryContractFactory {
return requireGeneratedSource(
contract,
key,
DataVersion.getLatest().get(),
DataVersion.getRuntime().get(),
IrisPlatforms.get().registries().generationRegistry()
);
}
@@ -652,7 +652,7 @@ public final class GenerationRegistryContractFactory {
return requireGeneratedSource(
contract,
key,
DataVersion.getLatest().get(),
DataVersion.getRuntime().get(),
generationRegistry
);
}
@@ -171,7 +171,7 @@ public final class SavedTerrainChunk {
case "biomes" -> 3;
case "noise" -> 4;
case "surface" -> 5;
case "carvers" -> 6;
case "carvers", "terrain" -> 6;
case "features" -> 7;
case "initialize_light" -> 8;
case "light" -> 9;
@@ -1,6 +1,9 @@
package art.arcane.iris.world.history;
import art.arcane.volmlib.util.nbt.io.NBTUtil;
import art.arcane.iris.spi.IrisPlatforms;
import art.arcane.iris.spi.PlatformBlockState;
import art.arcane.volmlib.util.nbt.mca.MCABlockStateCodecSupport;
import art.arcane.volmlib.util.nbt.tag.CompoundTag;
import art.arcane.volmlib.util.nbt.tag.ByteArrayTag;
import art.arcane.volmlib.util.nbt.tag.ListTag;
@@ -81,7 +84,8 @@ final class SavedTerrainChunkReader {
if (sectionY * 16 < minimumY || sectionY * 16 >= (long) minimumY + height) {
continue;
}
if (sections.put(sectionY, decodeSection(section)) != null) {
if (sections.put(sectionY, decodeSection(section, root.getInt("DataVersion") >= 5023
? MCABlockStateCodecSupport.Format.LOWERCASE : MCABlockStateCodecSupport.Format.CAPITALIZED)) != null) {
throw new IOException("Saved chunk contains duplicate section " + sectionY);
}
}
@@ -158,16 +162,29 @@ final class SavedTerrainChunkReader {
return section;
}
private static Section decodeSection(CompoundTag section) throws IOException {
private static Section decodeSection(CompoundTag section, MCABlockStateCodecSupport.Format format) throws IOException {
CompoundTag blocks = compound(section, "block_states");
CompoundTag biomes = compound(section, "biomes");
ListTag<?> blockPalette = list(blocks, "palette");
List<BoundaryColumnGeometry.Voxel> decodedBlocks = new ArrayList<>(blockPalette.size());
for (Tag<?> entry : blockPalette) {
if (!(entry instanceof CompoundTag state)) {
throw new IOException("Saved block palette entry is not a compound");
Tag<?> state = entry;
if (format == MCABlockStateCodecSupport.Format.LOWERCASE
&& state instanceof CompoundTag wrapped && wrapped.size() == 1 && wrapped.containsKey("")) {
state = wrapped.get("");
}
if (format == MCABlockStateCodecSupport.Format.LOWERCASE && state instanceof StringTag name) {
PlatformBlockState resolved = IrisPlatforms.get().registries().blockOrNull(name.getValue());
if (resolved == null) {
throw new IOException("Saved block state cannot be resolved: " + name.getValue());
}
decodedBlocks.add(decodeBlock(MCABlockStateCodecSupport.encodeBlockState(
resolved.key(), name.getValue(), format), format));
} else if (state instanceof CompoundTag compound) {
decodedBlocks.add(decodeBlock(compound, format));
} else {
throw new IOException("Saved block palette entry is not a block state");
}
decodedBlocks.add(decodeBlock(state));
}
ListTag<?> biomePalette = list(biomes, "palette");
List<String> decodedBiomes = new ArrayList<>(biomePalette.size());
@@ -181,10 +198,10 @@ final class SavedTerrainChunkReader {
new Palette<>(decodedBiomes, packedData(biomes), 1, 64));
}
private static BoundaryColumnGeometry.Voxel decodeBlock(CompoundTag state) throws IOException {
String name = string(state, "Name");
private static BoundaryColumnGeometry.Voxel decodeBlock(CompoundTag state, MCABlockStateCodecSupport.Format format) throws IOException {
String name = string(state, format.nameKey());
TreeMap<String, String> properties = new TreeMap<>();
if (state.get("Properties") instanceof CompoundTag values) {
if (state.get(format.propertiesKey()) instanceof CompoundTag values) {
for (Map.Entry<String, Tag<?>> property : values) {
if (!(property.getValue() instanceof StringTag value)) {
throw new IOException("Saved block property is not a string");
@@ -192,6 +209,13 @@ final class SavedTerrainChunkReader {
properties.put(property.getKey(), value.getValue());
}
}
return decodeBlock(name, properties);
}
private static BoundaryColumnGeometry.Voxel decodeBlock(String name, Map<String, String> properties) throws IOException {
if (name.isBlank()) {
throw new IOException("Saved block state has no resource key");
}
String stateKey = stateKey(name, properties);
if (name.equals("minecraft:air") || name.equals("minecraft:cave_air") || name.equals("minecraft:void_air")) {
return name.equals("minecraft:air") ? AIR
@@ -19,6 +19,8 @@
package art.arcane.iris.world.storage.region;
import art.arcane.iris.platform.bukkit.BukkitBlockResolution;
import art.arcane.volmlib.util.nbt.mca.MCABlockStateCodecSupport;
import art.arcane.iris.platform.bukkit.nms.datapack.DataVersion;
import art.arcane.iris.spi.IrisLogging;
import art.arcane.iris.platform.bukkit.nms.INMS;
@@ -30,6 +32,7 @@ import art.arcane.volmlib.util.nbt.mca.NBTWorldSupport;
import art.arcane.iris.platform.reflect.KeyedType;
import art.arcane.volmlib.util.math.M;
import art.arcane.volmlib.util.nbt.tag.CompoundTag;
import art.arcane.volmlib.util.nbt.tag.Tag;
import art.arcane.iris.generation.concurrent.HyperLock;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
@@ -43,7 +46,7 @@ import java.util.Locale;
public class NBTWorld {
private static final class Holder {
private static final BlockData AIR = BukkitBlockResolution.get("AIR");
private static final NBTWorldSupport.BlockStateCodec<BlockData> BLOCK_STATE_CODEC = NBTWorldSupport.blockStateCodec(
private static final NBTWorldSupport.BlockStateCodec<BlockData> BLOCK_STATE_CODEC = NBTWorldSupport.blockStateCodec(new NBTWorldSupport.BlockStateCodecOptions<>(
blockStateString -> BukkitBlockResolution.getOrNull(blockStateString, true),
BukkitBlockResolution::getAir,
blockData -> blockData.getAsString(true),
@@ -53,8 +56,11 @@ public class NBTWorld {
return "minecraft:" + blockData.getMaterial().name().toLowerCase(Locale.ROOT);
}
return key.getNamespace() + ":" + key.getKey();
}
);
},
INMS.get().getDataVersion() == DataVersion.V26_3
? MCABlockStateCodecSupport.Format.LOWERCASE
: MCABlockStateCodecSupport.Format.CAPITALIZED
));
private static final Map<Biome, Integer> BIOME_IDS = computeBiomeIDs();
}
@@ -127,7 +133,7 @@ public class NBTWorld {
);
}
public static BlockData getBlockData(CompoundTag tag) {
public static BlockData getBlockData(Tag<?> tag) {
return Holder.BLOCK_STATE_CODEC.decode(tag);
}