Retromod

Retromod Is a Minecraft mod that allows you to play older mods on newer versions.

File Details

Retromod 1.3.0-Snapshot.1 (NeoForge 1.21.11)

  • B
  • Jul 13, 2026
  • 3.25 MB
  • 3
  • 1.21.11
  • NeoForge

File Name

retromod-1.3.0-snapshot.1+1.21.11.jar

Supported Versions

  • 1.21.11

Curse Maven Snippet

NeoForge

implementation "curse.maven:retromod-1532616:8422897"
Curse Maven does not yet support mods that have disabled 3rd party sharing

Learn more about Curse Maven

First snapshot of the 1.3.0 line ("mixin translation"). The theme: stop soft-failing mixins and actually translate them. Where 1.2.0 could only STRIP a mixin handler whose target drifted (the feature went inert), 1.3.0 rewrites the handler so the feature keeps working. This snapshot lands the first batch of real translations, retiring four blocklist soft-fails, plus two regression fixes to the re-signature engine caught in review.

Added

  • ValueIO save-data adapter (#48). A 1.21.1 mod's @Inject into Entity.addAdditionalSaveData/readAdditionalSaveData (or the BlockEntity saveAdditional/loadAdditional) captures a CompoundTag; the 1.21.5 ValueIO refactor made those methods pass ValueOutput/ValueInput, so Mixin rejected the handler (InvalidInjectionException) and NeoForge cascaded into "broken mod state". MixinValueIoAdapter keeps the mod's handler body unchanged (still operating on a real CompoundTag) and wraps it: the synthesized handler converts the ValueIO param to a CompoundTag through a reflective, Minecraft-free runtime bridge (ValueIoBridge, per-mod-embedded on NeoForge/Forge for JPMS split-package safety). Repair-or-strip: if frame recomputation fails, the handler is stripped (the old soft-fail), so it can never ship a VerifyError. Retires the Darker Depths PlayerMixin blocklist entry.
  • @Inject / @Redirect / @WrapOperation / @Overwrite signature-drift re-signaturing (#69). The 1.21.5 "world/level threading" refactor prepended a ServerLevel to many LivingEntity/Entity/Mob/Player methods (doHurtTarget(Entity) becomes doHurtTarget(ServerLevel, Entity), and so on). MixinHandlerResignature inserts the new leading param into the handler descriptor, shifts every local slot, and rewrites the drifted @At(INVOKE) injection-point targets, so the handler matches the modern method again. Gated to 1.21.5+ hosts (a no-op where the signatures are intact), re-emitted under COMPUTE_FRAMES with a fall-back to the prior soft-fail. Retires Revamped Phantoms' SweepAttackMixin doHurtTarget entry.
  • MixinExtras injector dispatch. @ModifyReturnValue/@ModifyExpressionValue/@WrapOperation/... selectors are now routed through the same intermediary-to-Mojang / drift remap as core injectors, with the require=0 soft-fail net added only when a selector was actually rewritten (so a working MixinExtras mixin stays byte-identical).
  • Revamped Phantoms PhantomMixin (#50): a real translation via a deleted-superclass owner-alias. FlyingMob was deleted in ~1.21.2 and Phantom now extends Mob directly, but Phantom STILL declares getDefaultDimensions(Pose) (its body calls Mob.getDefaultDimensions, the former super), so the mod's @ModifyExpressionValue's only break was its @At(INVOKE) target still naming FlyingMob. A FlyingMob.getDefaultDimensions to Mob.getDefaultDimensions method redirect (in the three 1.21.11 to 26.1 shims, intermediary key on Fabric) re-owns the injection point, so the phantom-size feature works instead of being stripped. Retires the PhantomMixin blocklist entry. Known limitation: on the rarely-used intermediate host range 1.21.2 to 1.21.10, where FlyingMob is already gone but that redirect (and the intermediary to Mojang remap) does not apply, the feature hard-fails rather than soft-failing; the maintained 26.1+ path is fully covered, and pre-26.1 hosts are the #55 bridge's scope.
  • Mixin selector remapping is now applied on ALL loaders and the offline CLI, not just the Fabric runtime. Previously the @Mixin/@At/method= selector remap (method + class redirects) ran only inside the Fabric-runtime path; the NeoForge/Forge and CLI-batch paths did the blocklist strip + re-signature but skipped the selector remap, so an API-rename redirect (the #50 FlyingMob alias, the #28 Painting class-move on the NeoForge Deeper and Darker, the GameNarrator/setScreen scan-renames) never reached a NeoForge/Forge or offline-transformed mixin's annotation strings. Those paths now run the same full pipeline. (Without this, #50 and the class-moves would have silently regressed to hard injection failures on their actual loaders.)
  • Scan-rename redirects (from a 19-jar corpus scan of mixin selectors against the 26.x jars): GameNarrator.sayNow to saySystemNow, FriendlyByteBuf.readJsonWithCodec to readLenientJsonWithCodec (26.1 epoch), Minecraft.setScreen to setScreenAndShow (26.2 epoch).
  • 1.21.11 to 26.1 vanilla class moves: entity/decoration/Painting + PaintingVariant into entity/decoration/painting/, entity/monster/Husk into entity/monster/zombie/, and the MobSpawnType to EntitySpawnReason rename (all verified against the 26.1 and 26.2 jars).

Fixed

  • #28 (Deeper and Darker): a stale blocklist entry that protected nothing. The #28 entry named a PaintingItemMixin/deeperdarker$decrementStackOnServer handler that does NOT exist in the current jar (a silent no-op that hid the mod's real drift). Replaced with entries for the REAL classes: HangingEntityItemMixin.appendHoverText (its target was re-signatured List<Component> into TooltipDisplay + Consumer, a param split, and its body reads the deleted Painting.VARIANT_MAP_CODEC/CustomData.read, so it is genuinely unrepairable and is stripped, the variant tooltip going inert) and PaintingMixin.dropItem (a ServerLevel signature drift on a generic name, stripped). The Painting class-move fixes the @Mixin target so PaintingMixin.getPickResult now applies natively.
  • Re-signature engine: a @Local captured after the CallbackInfo trailer could turn a soft-fail into a hard crash. The parameter-annotation decline guard was bounded at the CallbackInfo index, but MixinExtras @Local/@Share locals sit AFTER it, and the re-signature does not shift the parameter-annotation arrays, so an inserted leading param misaligned the @Local onto the wrong parameter, an InvalidInjectionException (which COMPUTE_FRAMES can't catch) on exactly the #69 doHurtTarget-family methods. The guard is now full-width, so any parameter-annotated handler declines and keeps its soft-fail.
  • Re-signature engine: the @Inject method= selector was rewritten decoupled from the handler re-signature. rewriteAnnotationDrift eagerly rewrote an @Inject's top-level method= selector to new-form even when insertParams later declined, shipping a new-form selector against an un-re-signatured handler (a hard descriptor mismatch). The top-level @Inject method= rewrite is now owned solely by the coupled insertParams path; the @At target= injection-point rewrites (independent of the handler signature) are unchanged.
  • Owner-only mixin method redirects were silently dropped. The mixin target-redirect builder only recorded a redirect when the method NAME changed, so an owner-only redirect (the #50 FlyingMob.getDefaultDimensions to Mob.getDefaultDimensions alias) never reached the @At/method= remap. It now records the owner-qualified form for ANY change (owner, name, or descriptor); the owner-agnostic bare-name form still requires a genuine name change on a globally-unique obfuscated name.

Research (documented, not yet shipped)

  • True Darkness (#68) confirmed genuinely un-translatable. LightTexture was deleted and redesigned into Lightmap (a GPU texture / UBO produced by a separate LightmapRenderStateExtractor), so the mixin's @Shadow of the old CPU DynamicTexture field has no equivalent and the flicker/dirty state moved to a different class. A real fix is porting the mod to the 26.x render pipeline, outside a mechanical transformer. The whole-class strip stays.
  • Yung's API BeardifierMixin/NoiseChunkMixin confirmed repairable, held pending verification. Research (javap on 26.1/26.2) confirmed both are mechanically repairable (Beardifier: un-strip the mixin, strip only the dead BeardifierAccessor; NoiseChunk: demote the removed @Shadow @Final noiseSettings to a @Unique field captured from the surviving constructor param). They are HELD in this snapshot because re-enabling interdependent worldgen mixins requires a headless-server worldgen pass first (a mis-repair cascades to all chunk generation). The blocklist reasons carry the full mechanism.