Fix DeferredHolderCommon delegates not being references on Forge - #245
Merged
Merged
Conversation
Registry#safeCastToReference only accepts Holder.Reference, which is why DeferredHolderCommon exposes its bound holder as delegate. On Forge, the IForgeRegistry bind override ran before the regular registry lookup and bound to Holder.direct, so the delegate was never a reference and serialization kept failing there. The regular registry is now consulted first, and the Forge override prefers the registry's own delegate. Covered by game tests on all loaders. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WyxzX1DGvXj5nV4zew3V8u
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Background
Two earlier fixes made
DeferredHolderCommonbehave for other mods and for serialization:IHolderCommon#getDelegateplus theMixinRegistry/MixinHoldermixins, so thatRegistry#safeCastToReferencecan unwrap aDeferredHolderCommoninto theHolder.Referenceit wraps. NeoForge does this natively throughIHolderExtension#getDelegate; Fabric and Forge get it from the mixins.Both were applied to all three loaders at the time, so there is nothing left to port as such. The delegate fix does not take effect on Forge though, because of an older Forge-only addition:
DeferredHolderCommon.BIND_OVERRIDE, which Forge installs to resolve entries fromIForgeRegistrys. It is consulted before the regular registry and binds toHolder.direct(value).Since a
Holder.Directis not aHolder.Reference, the delegate never satisfiessafeCastToReference, and Forge keeps hitting exactly the failure the delegate fix was meant to remove. Forge wraps many vanilla registries (particle_type,menu,block,item,block_entity_type, ...), so the override is hit for most entries. Reproduced with the new game tests on Forge before this change:Changes
DeferredHolderCommon#bindconsults the regular registry first and only falls back toBIND_OVERRIDEfor entries that are not in a regular registry. This matches NeoForge's upstreamDeferredHolder, which binds through the registry only, and yields the canonicalHolder.Reference.BIND_OVERRIDEprefersForgeRegistry#getDelegate, which returns aHolder.Reference, and only falls back toHolder.directfor registries without a wrapper (those are not in the root registry, so they cannot be serialized through a registry codec anyway).DeferredHolderCommonTestgame tests, registered on all three loaders, asserting that the delegate is a reference and that the holder encodes through a registry holder codec.Testing
./gradlew buildpasses../gradlew runGameTestServerpasses on Forge, NeoForge and Fabric. Before the change, the two new tests failed on Forge and passed on NeoForge and Fabric.🤖 Generated with Claude Code
https://claude.ai/code/session_01WyxzX1DGvXj5nV4zew3V8u
Generated by Claude Code