Retromod 1.3.0-snapshot.6 (NeoForge 1.21.4)
Curse Maven Snippet
What's new
Sixth snapshot of the 1.3.0 line.
Added
- Mixins can now follow safe target-signature changes automatically. After normal namespace and shim remapping, Retromod checks each unresolved target against the exact methods declared by the current Minecraft jar. When one unique method preserves the old parameters in order and only adds up to three new parameters, it updates the selector and inserts unused handler parameters while preserving stack frames, locals, nullability annotations, and type annotations. Fabric refmap values, descriptor-shaped lookup keys, and existing official sections follow the same exact decision. This covers descriptor and bare-name
@Injecttargets, nested call targets used by@Redirectand MixinExtras@WrapOperation,@Overwritemethods, zero-capture HEAD/TAIL/RETURN injections, and tightly gated@Invokerrenames. Ambiguous overloads, deleted or reordered parameters, type substitutions, changed return values, class-level, injector-level, or nested@Atremap = false, and semantic local captures remain untouched. Verified against the exact No Chat Reports, Simple Voice Chat, Mouse Tweaks, and Dynamic FPS jars, plus a Fabric test-mod mixin compiled for 1.20.1 and focused refusal regressions. - Offline transforms can use the same exact host method index.
transform,batch, andaotnow accept--mc-jar <target.jar>. The CLI readsversion.jsonfrom that jar, unless--targetwas supplied, so version-gated shims and the method index cannot silently disagree. Loader transforms continue to discover and index the running Minecraft jar automatically.
Fixed
- Release builds now regenerate the external SHA-256 manifest.
build-all.shreplaces the stale checksum file with one entry for every current distribution jar and refuses a release matrix whose checksum count is incomplete. Snapshot.6 therefore publishes 69 checksums for its 69 artifacts: 68 loader jars and one CLI jar. - Offline transforms reject a target version that disagrees with
--mc-jar. If the Minecraft jar exposes a version, an explicit--targetmust now match it instead of selecting one shim chain while indexing a different host API. Jars without readable version metadata still support an explicit target and report that inference was unavailable. Covered by focused CLI regressions. - Batch verification now covers every produced jar. Verification also runs for unchanged copies, so
--verifyno longer leaves a blind spot when metadata says a mod already targets the selected host. - The first Fabric pre-launch uses the documented config defaults. Retromod now creates its default config before processing
retromod-input/, so transform verification and the other defaults apply on the first launch instead of only after normal mod initialization. - Saving the in-game config no longer discards manual settings. The screen shows the four settings that currently affect runtime behavior and merges those edits into the existing JSON. Reserved fields, the manual Modrinth opt-in, explanatory fields, and future keys survive a save. The
polyfills_enabledswitch is now honored during Fabric pre-launch and on Forge as well as NeoForge. Covered by config parsing and round-trip regressions. - The self-integrity check resolves encoded jar paths. A normal Prism Launcher location such as
Application Supportappears in a code-source URL with%20. Treating that URL text as a filesystem path made the release verifier returnUNKNOWNwithout its expected startup line. The verifier now resolves file and nested jar URLs throughURI, with regressions for encoded spaces, class directories, and non-file URLs. - Dynamic FPS mixins cross both of its reported 26.2 signature changes. An exact-prefix
@Injecthandler can now gain trailing target parameters before itsCallbackInfo, which repairs the added integer parameter onSoundEngine.play. Source metadata such as1.21.0is also normalized to the shim graph's1.21milestone, so the full chain runs andWindow.getWindow()reachesWindow.handle(). The exact mod passed both repaired mixin checks in a live 26.2 launch. Tested with focused descriptor, shim-chain, and Fabric load-time regressions. - Mixin shadow and accessor fields are resolved against their declared target owner. A redirect for
Window.windowpreviously renamed an unrelatedMinecraft.windowshadow because both fields shared a bare name. Field declarations, self-owned bytecode references, accessors, and explicit@At(FIELD)selectors now use owner and descriptor together, and field-to-method redirects are rejected. Tested with matching, unrelated-owner, descriptor-mismatch, and explicit-owner cases. - No Chat Reports crosses two removed client APIs on 26.2. Its chat options mixin now rebases from
SimpleOptionsSubScreentoOptionsSubScreenand adjusts the old constructor call. The post-remap repair also recognizes the temporary placeholder used by the Fabric runtime path, while that placeholder stays disabled on pre-26.1 hosts where the intermediary class is still live. Its deleted client-command parser invoker is replaced with a default interface bridge that parses through the current command dispatcher and suggestions provider. The exact mod passed both repaired mixin checks in a live 26.2 launch, with Fabric load-time regressions for each bridge. - Legacy Fabric client tick callbacks fire again. The callback bridge created and accepted old listener registrations, but tried to invoke
registerthrough Fabric API's package-private event implementation. Current Java rejected that reflective call, leaving the listeners inert. The bridge now invokes the publicEventinterface method. Covered by a regression with a private event implementation and a 26.2 title-screen launch with Chat Bubbles installed. - Legacy overlay checks no longer crash on 26.2.
Minecraft.getOverlay()andsetOverlay()moved behind the client'sguiobject. Retromod now uses a generated receiver-hop bridge for both calls, with the exact Chat Bubbles 1.0.1 bytecode and the real 26.2 field and method descriptors covered by focused tests. This does not make Chat Bubbles fully compatible: its only chat-capture mixin and its player-renderer replacement target removed systems, so the bubble feature still needs a manual port. - Fabric mods keep the live
BlockState.getBlock()method. A stale shim renamed it to the nonexistentgetBlockType()after the intermediary map had already selected the correct method, which made the test mod fail its block-state round trip at initialization. The bad override is removed and the full Fabric registration order is covered by a regression. - Constructor redirects can no longer fall into ordinary method heuristics. JVM constructors and class initializers are excluded from fuzzy and pattern-based method matching, which could otherwise leave an uninitialized object on the stack. Fifteen legacy shim registrations now use the dedicated constructor factory API or a class move, making the intended stack rewrite explicit. Tested with heuristic refusal cases and a registration audit across the shim set.
- Generated static Mixin helpers are accepted by current Mixin. Pose-constructor and worldgen-codec invokers become mixin-owned helpers after their descriptor bridge is generated. Leaving those helpers public made Mixin reject the class before it could merge them. Static helpers are now private, which also gives them the intended access to the target's private constructor or registrar. Covered by focused access regressions and the Fabric load-time test mixins.
- Relocated Fabric callback bridges keep each mod's real interface identity. Per-mod synthetic interfaces are intentionally relocated to avoid JPMS split packages, but the item-group, server-world, and entity-model-layer bridges still created events or reflective methods from their original generated interfaces. That produced
ArrayStoreExceptionduring registration or anIllegalArgumentExceptionduring resource reload. Generated holders now pass remappable class literals, caches include the relocated type, and delayed providers resolve the interface implemented by the callback object. Tested through actual synthetic embedding, deferred invocation, and the Fabric in-game bridge harness. - Generated compatibility helpers are isolated across every nested-jar path. Fabric previously copied shared
com/retromod/generatedclasses into every transformed mod, so an older jar could win Knot's class lookup and expose a stale bridge signature to newly transformed mods. Fabric now embeds only the helpers a mod references under a stable per-mod name, while old no-argument callback holders remain supported. Fabric jar-in-jar, CLI, and AOT recursion apply the same relocation inside each nested library. If relocation cannot complete, the original nested jar is kept byte-for-byte instead of publishing dangling helper references. Tests cover recursive keys, manifests, directory entries, collisions, archive limits, and old and new bridge descriptors. - Multiple translated HUD callback mods can render together. Every relocated
HudRenderCallbackpreviously registered the sameretromod:legacy_hud_renderlayer id, so Fabric accepted the first mod and left later callbacks inert. Each callback type now receives a stable, valid, collision-resistant layer path derived from its relocated identity. Tested with two independent callback types and a full synthetic-bridge audit. - Mixed-loader offline batches no longer leak member namespaces between jars. The singleton transformer could leave Forge target-SRG names active for the next Fabric mod, or Fabric intermediary names active for the next Forge mod. Loader member mappings are now rebuilt for each input jar. The same shared setup also reaches standalone AOT and
batch --aot, which previously missed the new Forge 1.20.1 target-SRG layer entirely. Tested in both loader orders and through the public AOT jar path with an exactfunc_180495_ptom_8055_call. - Forge and NeoForge refmap repair now reaches AOT output and nested jars. Refmap selectors are rewritten on the CLI, normal AOT, and recursive jar-in-jar paths with the same loader gate as the outer runtime transform. Fabric intermediary sections remain unchanged. Tested across outer and nested AOT resources and CLI nested jars.
- Old Forge member names now cross into the exact Forge 1.20.1 SRG namespace (#192). A 1.16 mod uses
func_andfield_names, while Forge 1.20.1 expects differentm_andf_identifiers. Mapping the old names only to Mojang names works on unobfuscated 26.x but fails on pre-26 Forge. Retromod now joins source SRG, Mojang official names, and Forge 1.20.1 MCPConfig with owner and descriptor qualification, covering 39,079 methods and 30,036 fields without global-name guesses. Better Portals' reported block-state and entity-level references now resolve to the exact Forge 1.20.1 members. Unsupported Forge targets keep their source names instead of receiving a half-correct Mojang conversion. Tested with generated mapping-table validation, focused transform regressions, and the exact Better Portals 0.3.9 jar. - Forge mixin refmaps follow class and member remaps (#192). Rewriting a mixin class is not enough because Forge resolves its annotation through a separate refmap resource. Better Portals' class annotation targeted
LocalPlayer, but its refmap still supplied the removedClientPlayerEntityowner and Mixin rejected the handler. Refmap keys and values now move together through owner-qualified class, descriptor, and target-SRG mapping on the CLI and runtime Forge paths, including nested jars. The reported client-player and portal-shape mixins now pass Mixin parsing on Forge 1.20.1. Tested with the exact two-entry refmap and a real client launch. - Legacy
BlockBehaviour.Properties.noDrops()calls usenoLootTable()on Forge 1.20.1 (#192). The rename is owner-scoped and then resolved to the target SRG name, so unrelated methods are untouched. The reported mod now passes this construction call instead of failing withNoSuchMethodError. Tested with a focused mapping regression and a real client launch.
Compatibility reports
- Compatibility analysis now reports eager legacy Forge registration. A mod that combines
RegistryEvent.Registerhandlers with non-JDK object construction in static initializers is flagged as needing aDeferredRegisterlifecycle migration. This catches the next Better Portals blocker, where its symbols all resolve but Forge rejects block construction after the registry is frozen. The analyzer now explains that structural limit instead of leaving users with only a clean linkage report. Tested with a synthetic legacy registry holder and the exact reported jar.
Known limitations
- A refmap-only Yarn method rename cannot yet supply newly captured handler parameters. Retromod repairs the refmap target, so zero-capture injections keep working. If the annotation text still uses a different Yarn source name and its handler captures every old target parameter, the class pass cannot yet connect that source name back to the repaired refmap entry. These handlers remain unchanged for a reviewed repair instead of receiving a guessed parameter layout.
- YUNG's Better Portals still needs a registry lifecycle port on Forge 1.20.1 (#192). Snapshot.6 clears the missing class, old-to-target SRG members, stale mixin refmap, and removed
noDrops()call. The exact mod and YUNG's API then reach mod construction, where Better Portals eagerly creates its blocks and fluids from static initializers. Forge 1.20.1 requires those objects to be supplied during its registry event throughDeferredRegister; translating static fields, their consumers, fluids, blocks, items, and event timing is a structural mod port rather than a safe member redirect. The new analyzer reports this explicitly.
Infrastructure
- Forge 1.20.1 target mappings are reproducible.
scripts/harvest-target-srg.pyjoins Mojang's official mappings with Forge MCPConfig and writes the owner-and-descriptor-qualified target table used by the transformer. - The release matrix now covers every published target again. NeoForge 26.1 and 26.1.1 are no longer skipped, and the script requires exactly 23 Fabric, 23 Forge, 22 NeoForge, and one CLI artifact. A complete snapshot.6 run produced all 69 jars with no failures.
This mod has no additional files

