Skip to content

Update to Minecraft 26.3 (14 Fabric game tests regress) - #211

Merged
rubensworks merged 6 commits into
master-26from
claude/minecraft-26-3-update-xr1cny
Sep 20, 2026
Merged

rubensworks merged 6 commits into
master-26from
claude/minecraft-26-3-update-xr1cny

Conversation

@rubensworks

@rubensworks rubensworks commented Sep 20, 2026

Copy link
Copy Markdown
Member

Updates all three loaders to Minecraft 26.3, alongside CyclopsMC/CyclopsCore#243 and CyclopsMC/CommonCapabilities#50. All game tests pass on all three loaders (95 on NeoForge and Forge, 96 on Fabric).

Fixed: 14 upgrade/downgrade tests failed on Fabric only

Every testcolossal*3x3upgrade and *3x3downgrade test failed on Fabric with GameTestAssertException: Interaction must succeed, while NeoForge and Forge passed.

This was a bad assertion in the tests, not a defect in the mod. They compared the result of ItemStack#useOn against the InteractionResult.SUCCESS constant by identity. In 26.3, vanilla's ItemStack#useOn no longer returns the item's result unchanged: when the result is a Success with wasItemInteraction set, it returns success.heldItemTransformedTo(stack), a new Success record carrying the held stack. Since Success is a record with a now non-null heldItemTransformedTo, neither == nor equals matches the constant.

NeoForge and Forge patch ItemStack#useOn to route server-side use through CommonHooks#onPlaceItemIntoWorld, which skips that transformation, which is exactly why only Fabric failed.

The assertions now check for an InteractionResult.Success instead. The two assertions on FAIL are unchanged, since non-Success results are still passed through untouched.

An earlier revision of this description blamed consumeItems rejecting a valid inventory. That was wrong: instrumenting attemptTransform showed the only test reaching that path is the wood case that is supposed to fail with 22 walls. The upgrade tool returned SUCCESS and transformed the chests correctly on Fabric all along.

Tooling

  • Gradle wrapper 9.5.0 → 9.7.1. Fabric Loom 1.18 requires it, and Loom also requires the build JVM itself to be Java 25 (CI already provisions Java 25).
  • Fabric Loom 1.17-SNAPSHOT1.18.2, NeoForge ModDev 2.0.1412.0.147.
  • NeoForge 26.3.0.7-beta, Forge 26.3-66.0.2, Fabric API 0.161.0+26.3, Fabric Loader 0.19.5, NeoForm 26.3-1, Forge Config API Port 26.3.0.

Code

  • The block type MapCodec registry and Block#codec were removed, so the codec fields and codec() overrides are dropped from the three blocks.
  • ContextAwarePredicate is gone, so ChestFormedTrigger holds an Optional<Holder<LootItemCondition>>.
  • BlockPos no longer has a Vec3i constructor.
  • PoseStack#mulPose(Quaternionf) replaced by rotateDegrees.
  • submitModel no longer takes the crumbling overlay; it goes to submitCrumblingOverlay, mirroring vanilla's ChestRenderer.
  • FriendlyByteBuf#writeMap/readMap/limitValue are gone, so the changed slots go through a ByteBufCodecs.map codec. It keeps int keys rather than vanilla's shorts, since a colossal chest has far more than 32767 slots — worth a second opinion, as it is the one place I deliberately diverged from vanilla.

Data

  • The minecraft:placed_block trigger takes a single loot condition for "location" rather than a list, and block_state_property became match_block wrapping a BlockPredicate.
  • Missing item tags in recipe ingredients are a hard parse error now rather than being skipped, so an empty c:ingots/silver tag is shipped for the silver recipes to be conditionally skipped as intended.

CI is blocked on CommonCapabilities 26.3

cyclopscore_version is now 1.30.0-1159, the published 26.3 build, and resolves. neoforge_commoncapabilities_version still points at a 26.2 build and is the only remaining unresolved dependency, so CI fails on it until CyclopsMC/CommonCapabilities#50 is merged and published. Note that the pinned value is a placeholder and will need bumping to whatever version that run produces.

Locally this builds against a publishToMavenLocal CommonCapabilities through commoncapabilities_version_local in secrets.properties.


Generated by Claude Code

rubensworks and others added 5 commits September 20, 2026 08:32
Tooling, matching CyclopsCore:
- Gradle 9.7.1, needed by Fabric Loom 1.18 (which requires a Java 25 JVM
  for the build itself)
- Fabric Loom 1.18.2, NeoForge ModDev 2.0.147
- NeoForge 26.3.0.7-beta, Forge 26.3-66.0.2, Fabric API 0.161.0+26.3,
  Fabric Loader 0.19.5, NeoForm 26.3-1, Forge Config API Port 26.3.0

Code:
- The block type MapCodec registry and Block#codec were removed, so the
  codec fields and codec() overrides are dropped from the three blocks
- ContextAwarePredicate is gone, so ChestFormedTrigger holds an
  Optional<Holder<LootItemCondition>>
- BlockPos no longer has a Vec3i constructor
- PoseStack#mulPose(Quaternionf) replaced by rotateDegrees
- submitModel no longer takes the crumbling overlay; it goes to
  submitCrumblingOverlay, mirroring vanilla's ChestRenderer
- FriendlyByteBuf#writeMap/readMap/limitValue are gone, so the changed
  slots use a ByteBufCodecs map codec. It keeps int keys rather than
  vanilla's shorts, since a colossal chest has far more than 32767 slots

Data:
- The minecraft:placed_block trigger takes a single loot condition for
  "location" rather than a list, and block_state_property became
  match_block wrapping a BlockPredicate
- Missing item tags in recipe ingredients are a hard parse error now, so
  an empty c:ingots/silver tag is shipped for the silver recipes to be
  conditionally skipped as intended

Known issue: 14 upgrade/downgrade game tests fail on Fabric only, with
ItemUpgradeTool reporting "not enough items". NeoForge and Forge pass all
95. The same tests pass on the 26.2 baseline, so this is a genuine 26.3
regression rather than something pre-existing.

cyclopscore_version and neoforge_commoncapabilities_version still point at
26.2 builds, since no 26.3 builds are published yet. Building this needs
cyclopscore_version_local and commoncapabilities_version_local in
secrets.properties until then.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V35MFZ7JSgoLe79J4d1uxf
Appended automatically by clientdevbridge on first use, matching the
entry already present in CyclopsCore.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V35MFZ7JSgoLe79J4d1uxf
CyclopsMC/CyclopsCore#243 is merged, and its master-26 run published
cyclopscore 1.30.0-1159, so the placeholder version and the note about
building locally are no longer needed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V35MFZ7JSgoLe79J4d1uxf
CyclopsMC/CommonCapabilities#50 is merged and its master-26 run published
commoncapabilities 2.11.8-398.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V35MFZ7JSgoLe79J4d1uxf
The tests compared the result of ItemStack#useOn against the
InteractionResult.SUCCESS constant by identity. In 26.3, vanilla's
ItemStack#useOn no longer returns the item's result unchanged: for a
Success whose wasItemInteraction is set, it returns
success.heldItemTransformedTo(stack), a new Success record carrying the
held stack. So the constant never matches.

NeoForge and Forge patch useOn to route server-side use through
CommonHooks#onPlaceItemIntoWorld, which does not apply that
transformation, which is why only Fabric failed.

The upgrade tool itself was correct on all three loaders: it returned
SUCCESS and the chests were transformed. Only the assertion was too
strict, so it now checks for an InteractionResult.Success instead. The
two assertions on FAIL are left alone, since non-Success results are
still passed through unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V35MFZ7JSgoLe79J4d1uxf
this.changedSlots = changedSlots;
}

private static final int MAX_SLOT_COUNT = 128;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this here? Won't this cause slot id's above 128 to not be sent anymore?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question, but no: it caps the number of changed-slot entries in the map, not the slot id values. Slot ids well above 128 (and above 32767) are still sent.

The map's key codec is ByteBufCodecs.VAR_INT, so keys are full ints. maxSize is only ever used as an entry count — in ByteBufCodecs.map it is writeCount(output, map.size(), maxSize) on encode and readCount(input, maxSize) on decode. That is exactly what the override exists for, and why the line under it notes we keep int keys rather than vanilla's shorts.

It is also not new. On 26.2 this same 128 was already applied, just on the decode side only and unnamed:

IntFunction<Int2ObjectOpenHashMap<HashedStack>> intfunction = FriendlyByteBuf.limitValue(Int2ObjectOpenHashMap::new, 128);
this.changedSlots = Int2ObjectMaps.unmodifiable(input.readMap(intfunction, FriendlyByteBuf::readInt, ...));

I moved it into the stream codec and gave it a name, matching how vanilla now writes the same packet. Vanilla's ServerboundContainerClickPacket on 26.3 is:

private static final int MAX_SLOT_COUNT = 128;
private static final StreamCodec<RegistryFriendlyByteBuf, Int2ObjectMap<HashedStack>> SLOTS_STREAM_CODEC = ByteBufCodecs.map(
    Int2ObjectOpenHashMap::new, ByteBufCodecs.SHORT.map(Short::intValue, Integer::shortValue), HashedStack.STREAM_CODEC, 128
);

So this override is the same construction with VAR_INT keys where vanilla uses SHORT — which is the whole reason the override exists.

Two honest differences worth recording, neither changing the ceiling:

  • The cap now applies on encode as well as decode, because ByteBufCodecs.map calls writeCount with maxSize. Previously an oversized map would have encoded happily and blown up on the receiving side; now it fails on the sender. Same effective limit, better failure mode.
  • Keys go from a fixed 4-byte writeInt to VAR_INT. Both ends are this mod's own packet, so it stays self-consistent and is smaller on the wire for low slot ids.

If you would rather raise the entry cap above vanilla's 128 for colossal chests, that is a one-line change and I am happy to make it, but it would be a behaviour change rather than part of the port.


Generated by Claude Code

NeoForge 26.3 deprecated logoFile in favour of bannerFile and iconFile,
and warns about it on every dev client launch. The logo is square, so
iconFile is the fitting replacement.

Only the NeoForge manifest changes: Forge 26.3 still only understands
logoFile, so its mods.toml is left as is.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V35MFZ7JSgoLe79J4d1uxf
@rubensworks
rubensworks merged commit 8979110 into master-26 Sep 20, 2026
5 checks passed
@rubensworks
rubensworks deleted the claude/minecraft-26-3-update-xr1cny branch September 20, 2026 12:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants