16 KiB
85 - Maintainer — Minecraft Version Bump
gradle.properties minecraftVersion is the single source of truth for the target Minecraft version. Most build outputs derive from it. This document lists every edit required to move Iris to a new Minecraft version, in order.
How to run the bump
Use a dedicated branch or worktree and begin from a green build. Record the old and new Minecraft, Bukkit API, loader, datapack-format, and NMS binding values before editing. Complete the ordered steps without skipping ahead, run the focused check after each platform boundary, then run the full all-platform build and the disposable-server lifecycle runbooks.
Stop when an upstream API, mapping, or loader artifact is unavailable; do not hide the gap behind a legacy fallback. The bump is complete only when generated artifact names, metadata ranges, data fixers, NMS selection, tests, and documentation all agree on the same target.
Source of truth
gradle.properties:
minecraftVersion— target MC version (e.g.26.2). DrivesBuildConstants.MINECRAFT_VERSION, thecom.mojang:minecraftcoordinate, all mod-metadata minecraft ranges, and the three loader jar names. The CraftBukkit jar name comes frombukkitMinecraftRangeinstead (step 1).apiVersion— Bukkit pluginapi-version(e.g.26.1). Deliberately decoupled fromminecraftVersion: it is the lowest Minecraft release line the single plugin artifact loads on (currently26.1so 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.irisVersion— bump the trailing-<mc>suffix to match (e.g.4.0.0-26.2->4.0.0-27.0).
Ordered steps
-
Edit
gradle.properties: updateminecraftVersion,fabricLoaderVersion,forgeVersion,neoForgeVersion, and theirisVersionsuffix. RevisitapiVersiononly if the bump drops support for the oldest Minecraft line the plugin artifact still loads on.bukkitMinecraftRangeis deliberately absent fromgradle.properties; it defaults to'26.1.2-26.2'inbuild.gradleand is the version string the CraftBukkit jar filename carries, so edit that default (or pass-PbukkitMinecraftRange=) whenever the supported Bukkit range moves. -
Edit
gradle/libs.versions.toml:paper-api— the compile pin for:coreand:adapters:bukkit:plugin(currently26.1.2.build.74-stable). LikeapiVersion, it is held at the lowest supported release line so one plugin jar loads across the whole range; raise it only when dropping that line.spigot— the Spigot API pin (<mc>-R0.1-SNAPSHOT). UsedcompileOnlyby the three modded adapters and:probeonly, never by the Bukkit plugin. Bump it withminecraftVersion.fabricApi-*— the ten Fabric API module versions, if the new MC requires different Fabric API builds. Each module is versioned independently (<version>+<build-hash>). The ten arebase,registrySync,resourceLoader,lifecycleEvents,commandApi,eventsInteraction,networking,rendering,keyMapping,permission. Every one of them is bundled jar-in-jar and must be declared infabric.mod.jsonjars— see step 7.
-
Edit
core/src/main/java/art/arcane/iris/core/nms/datapack/DataVersion.java(manual, structural):- Append a new enum constant named for the MC version with dots replaced by underscores:
V<mc>("<mc>", <packFormat>, <DataFixer>::new). The current tail isV26_1_2("26.1.2", 101, DataFixerV1217::new), V26_2("26.2", 107, DataFixerV1217::new). packFormatcomes from https://minecraft.wiki/w/Pack_format.getLatest()returns the last enum constant, so append at the end; do not reorder, and leaveUNSUPPORTEDfirst.- Reuse the existing fixer when the datapack format did not change (26.1.2 and 26.2 both use
DataFixerV1217). If it did change, add a newIDataFixerimplementation in a versioned subpackage undercore/src/main/java/art/arcane/iris/core/nms/datapack/(existing ones arev1192,v1206,v1213,v1217).
- Append a new enum constant named for the MC version with dots replaced by underscores:
-
Register and select the new Bukkit NMS binding. The revision tag is spelled out by hand in five places plus the sources; miss any one and the build fails or the plugin refuses to bind at boot.
settings.gradle— addinclude(':adapters:bukkit:nms:v<major>_<minor>_R<rev>').build.gradle— add the binding to thenmsBindingsmap:v<major>_<minor>_R<rev>: '<paper-dev-bundle-version>'. That value is passed to paperweight'spaperDevBundle(...), not to Spigot BuildTools; it is currently'26.1.2.build.74-stable', held at the same low pin aspaper-api.build.gradle— updateart/arcane/iris/core/nms/v26_2_R1/NMSBinding.classinrequiredBukkitArtifactEntriesto the new tag, orverifyBukkitArtifactfails against the shipped jar..github/workflows/ci.yml— update the hardcoded:adapters:bukkit:nms:v26_2_R1:testtask path in theverifyjob.core/src/main/java/art/arcane/iris/core/nms/NmsBindingSelector.java— the runtime half, fully hand-maintained. UpdateSUPPORTED_TAG,SUPPORTED_VERSIONS, and theisSameRelease(...)guard.INMS.bindExactreflects onart.arcane.iris.core.nms.<tag>.NMSBinding, so an unedited selector rejects the new server before the new binding is ever loaded. Updatecore/src/test/java/art/arcane/iris/core/nms/NmsBindingSelectorTest.javain the same edit; it asserts the tag and the accepted/rejected versions.- Create the binding sources under
adapters/bukkit/nms/v<major>_<minor>_R<rev>/src/main/java/art/arcane/iris/core/nms/v<major>_<minor>_R<rev>/, matching the existing set:NMSBinding,IrisChunkGenerator,CustomBiomeSource,VanillaStructureBiomes,DatapackStructureStateFilter,ImportedFeatureStage.
-
Update loader version-range metadata (manual floors/ranges only; the
minecraftranges are templated fromminecraftVersionand need no edit):adapters/fabric/src/main/resources/fabric.mod.json—minecraftis~${minecraftVersion}(auto). Update thefabricloaderfloor (currently>=0.19.3) if the loader minimum changes, and thejarslist if the bundled Fabric API modules change.adapters/forge/src/main/resources/META-INF/mods.toml—minecraftversionRange is[${minecraftVersion}](auto). UpdateloaderVersion(currently[65,66)) and theforgedependency versionRange (also[65,66)). Both are hand-maintained and both are upper-bounded, so both must move on every Forge line bump.adapters/neoforge/src/main/resources/META-INF/neoforge.mods.toml—minecraftversionRange is[${minecraftVersion}](auto).loaderVersion(currently[3,)) is the javafml specification version, not the NeoForge version, and rarely moves. TheneoforgedependencyversionRangeis hardcoded (currently[26.2,26.3)) and is not templated fromminecraftVersion— hand-edit it on every bump or the mod will refuse to load on the new NeoForge line.
-
Re-verify the mapping-coupled files. Six files name Mojang-mapped classes, fields, and method descriptors directly. Nothing templates them, nothing fails fast at build time if a name moved, and a stale entry surfaces as a silent no-op or a load-time crash. Check every one against the new MC jar.
Access widener (Fabric) —
accessWidener v2 official, so the names are Mojang-mapped:-
adapters/fabric/src/main/resources/irisworldgen.accesswidenerMinecraftServer.levelsLjava/util/Map;MinecraftServer.executorLjava/util/concurrent/Executor;MinecraftServer.storageSourceLnet/minecraft/world/level/storage/LevelStorageSource$LevelStorageAccess;PackRepository.sourcesLjava/util/Set;(accessible and mutable)
Verify: each field still exists with that exact descriptor. Loom fails the build on an unresolvable AW entry, so a rename shows up as an AW error — read it, do not delete the line.
Access transformers (Forge and NeoForge) — must stay in sync with each other and with the AW:
-
adapters/forge/src/main/resources/META-INF/accesstransformer.cfg -
adapters/neoforge/src/main/resources/META-INF/accesstransformer.cfg- both:
public net.minecraft.server.MinecraftServer levels/executor/storageSource
Verify: the three ATs match the first three AW entries. Note the ATs have no
PackRepositoryentry — Forge/NeoForge reach the pack sources through their own hooks, so do not add one without a reason. Wired viaminecraft { accessTransformer.from(...) }inadapters/forge/build.gradleandneoForge { accessTransformers.from(...) }inadapters/neoforge/build.gradle; NeoForge additionally declares the file in an[[accessTransformers]]block inneoforge.mods.toml. - both:
Mixin configs — three JSONs, eight mixin classes, all targeting Mojang-mapped members. The JSONs only list class names; the mapped method names and descriptors below live in the
.javamixin classes, so check those files, not the configs:-
adapters/fabric/src/main/resources/irisworldgen.mixins.json(packageart.arcane.iris.fabric.mixin,compatibilityLevelJAVA_21, Fabric only)BlockItemMixin->BlockItem.placeBlock,@At("RETURN")BlockMixin->Block.getDrops(...)with a full descriptor (BlockState, ServerLevel, BlockPos, BlockEntity, Entity, ItemInstance) — the highest-churn entry in the repo; the parameter list changes across MC versionsPackRepositoryMixin->PackRepository.<init>,@At("RETURN")
-
adapters/modded-common/src/main/resources/irisworldgen.entity.mixins.json(packageart.arcane.iris.modded.mixin,compatibilityLevelJAVA_21, all three loaders)EntityPersistenceMixin->Entity.shouldBeSavedLivingEntityLootMixin->LivingEntity.dropFromLootTable(ServerLevel, DamageSource, boolean)— full descriptorMobAwarenessMixin->Mob.serverAiStep, injecting at a field target (Lnet/minecraft/world/entity/Mob;noActionTime:I) — verify the field, not just the method
-
adapters/modded-common/src/main/resources/irisworldgen.client.mixins.json(packageart.arcane.iris.client.mixin,compatibilityLevelJAVA_21, classes listed underclientrather thanmixins)IrisWorldOpenFlowsMixin->WorldOpenFlows.confirmWorldCreationandWorldOpenFlows.openWorldCheckWorldStemCompatibilityIrisWorldTypeEntryMixin->WorldCreationUiState.WorldTypeEntry.describePreset, plus a@Shadowmember — shadows break silently if the field is renamed
The client mixin config lives in
modded-common/src/main/resourcesbut the classes live inadapters/client-common/src/main/java/art/arcane/iris/client/mixin/; the modded mixin classes live inadapters/modded-common/src/main/java/art/arcane/iris/modded/mixin/. All three adapters add both shared source dirs, so one edit hits every loader.Registration differs per loader and each place must list the same configs:
- Fabric —
fabric.mod.jsonmixins(all three; the client one gated on"environment": "client"). - NeoForge —
[[mixins]]blocks inneoforge.mods.toml(entity + client). - Forge — no toml entry; FML 26.2 reads neither
[[mixins]]noraccessTransformersfrom a Forge toml. The jar manifest attributeMixinConfigsinadapters/forge/build.gradleis the only shipping registration, and theminecraft { runs { … } }block passes--mixin.configseparately (clientgets entity + client,servergets entity only). Adding a mixin config on Forge means editing the manifest attribute and the relevant run args.
injectors.defaultRequireis1in all three configs, so a mixin that no longer applies fails the run instead of degrading quietly. Treat any "mixin apply failed" line as a bump blocker, and run bothrunClientandrunServerper loader — client-only mixins are not exercised by a server run.
-
-
Reconcile the Fabric jar-in-jar list across three places that must agree exactly.
adapters/fabric/build.gradleadds every Fabric API module to thejijconfiguration, andshadowJarcopies it intoMETA-INF/jars, renaming each artifact through thenestedFabricApiJarsmap (an explicit module-to-filename table, not a version-stripping regex — a bundled jar with no map entry fails the build withUndeclared Fabric API nested jar). Thejijconfiguration istransitive = false, so the bundled set is exactly the declared set. Adding or removing a module means editing thejijdependency list,nestedFabricApiJars, and thejarsarray infabric.mod.jsontogether. After changing the module list, confirm the jar agrees:unzip -l "dist/Iris v<version> [Fabric] <mc>+<loader>.jar" | grep META-INF/jarsAn entry in
jarswith no matching nested jar makes the loader refuse the mod; a nested jar missing fromjarsis dead weight the loader never mounts. -
Build and verify, in this order:
./gradlew :core:check— includesbukkitPurityRatchetand the NMS selector tests. Green before touching the platform builds../gradlew buildBukkit— runsverifyBukkitArtifactfirst, then drops the jar indist/../gradlew buildFabric./gradlew buildForge./gradlew buildNeoforge
Each
build*task runs its artifact verifier as a dependency, so a missing resource, an unapplied mixin config, or a new Bukkit-coupled class fails the task rather than shipping../build-all.shruns all four serialized with local VolmLib substitution off; use it for the final pass. Good looks like: four jars indist/carrying the new version strings, and aVerified …line for every platform. -
After a successful bump, re-run the operator runbooks and GoldenHash parity on all platforms (
31 - Operator Runbooks.md,32 - Determinism & Goldenhash.md) and continue with86 - Maintainer - Release Checklist.mdonly after87 - Maintainer - Release Readiness.mdallows GO or GO-WARN.
Derived automatically (do not hand-edit on a version bump)
- Bukkit plugin
api-version—adapters/bukkit/plugin/build.gradleexpandsapiVersioninto bothplugin.ymlandpaper-plugin.ymlatprocessResources. BuildConstants.MINECRAFT_VERSION— stamped by thegenerateTemplatestask incore/build.gradlefromminecraftVersion(template atcore/src/main/templates/BuildConstants.java); consumed byTasks.supportedVersions.- Mod-metadata
minecraftversion ranges — templated fromminecraftVersionatprocessResources. - The three loader jar names and the
com.mojang:minecraftcoordinate — composed fromminecraftVersionin the build scripts. The CraftBukkit jar name is the exception: it carriesbukkitMinecraftRange, notminecraftVersion(step 1).
Notes
build.gradle, the adapterbuild.gradlefiles, andsettings.gradlecarry defensive.getOrElse(...)defaults for the version properties.gradle.propertiesalways overrides them, so a bump does not require touching those fallbacks; refresh them only if the checked-in default should track the current release.bukkitMinecraftRangeis the one exception — it has nogradle.propertiesentry, so its'26.1.2-26.2'default is the live value.- The Java literals
"26.2"and"26.1.2"intentionally remain inDataVersion.java(structural enum constants),core/src/test/java/art/arcane/iris/core/nms/MinecraftVersionTest.java, andcore/src/test/java/art/arcane/iris/core/lifecycle/PaperLibBootstrapTest.java. The test files use MC version strings as parser fixtures, not as a version source; update them only when the version string formats they exercise change. NmsBindingSelector.javaandNmsBindingSelectorTest.javaalso hardcode MC version literals, but those are not intentional leftovers — they are required edits (step 4).