Retromod

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

File Details

Retromod 1.2.0-rc.1 (Fabric 26.2)

  • R
  • Jul 6, 2026
  • 3.22 MB
  • 18
  • 26.2
  • Fabric

File Name

retromod-1.2.0-rc.1+26.2.jar

Supported Versions

  • 26.2

Curse Maven Snippet

Fabric

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

Learn more about Curse Maven

First release candidate for 1.2.0 ("the general update"). Promotes the accumulated snapshot line (snapshot.1 through the unreleased snapshot.9) to a release candidate. Across the 1.2.0 line this delivers MC 26.1.x + 26.2 as the maintained targets on Fabric, NeoForge, and Forge (Forge 65.x, EventBus 6 -> 7), the Forge -> NeoForge migration spine, the 26.2 render-API stand-ins, worldgen on 26.x, and the transform-level 1.12.2 groundwork plus its load layer. The changes new since snapshot.8 are under Fixed below; this rc also closed a cleanup pass:

  • Phantom-target cleanup. (done) Deleted the 10 third-party API shims that were entirely phantom (AutoRegLibApiShim, JadeWailaApiShim, PatchouliApiShim, BaublesApiShim, ThermalApiShim, NeiApiShim, WailaApiShim, FabricShieldLibApiShim, OwoLibApiShim, LbaApiShim): every redirect they registered pointed at a never-written embedded class, so they were dead weight (removed their .java files and their META-INF/services/com.retromod.core.VersionShim entries). That takes the phantom count from 97 to 51. The remaining 51 live inside otherwise-valid core version shims (mixed with real registrations) and are paid down over time. New PhantomBaselineTest fences the set against phantom-baseline.txt so any NEW phantom fails the build (and fixing one prompts shrinking the baseline). The snapshot.8 runtime guard already made all of these fail-safe; this removes the underlying debt.
  • method_14452 PlacementModifier mapping fix. (done, see Fixed below.)
  • Multi-mod mcmod.info tomls. (done, see Fixed below.)

Deferred out of 1.2.0 (tracked, roll to a 1.2.x follow-up, not a blocker for this rc):

  • ClientPlayNetworking v1 raw-bytes bridge (#51), the §B3 armor-layer EquipmentClientInfo bridge (#70), and the Sinytra Tier B bundle (#94, covering #9/#11/#12/#13). These were staged at the transform level during the snapshot cycle but still need in-game launch validation on their target hosts, so they follow the rc rather than gate it.
  • Fully running real 1.12.2 mods (beyond the class-load + registration-event stubs this rc ships) is a per-mod chase through a decade of removed systems and earns its own release: the 1.4.0 line, "The 1.12.2 update", in ROADMAP.md.

Fixed

  • new ResourceLocation(namespace, path) no longer corrupts to a VerifyError (#135 Wyrms of Nyrus, #136, #121). The polyfill that rewrites the removed ResourceLocation constructors to the static factories registered them as plain method redirects, which only swapped the target and left the uninitialized NEW/DUP on the stack with an INVOKESPECIAL against the factory: VerifyError: Bad type on operand stack ("Type uninitialized 0 ... is not assignable") at class load in any mod building a ResourceLocation (the canonical 1.12.2 idiom). It now uses registerConstructorRedirect (which strips the NEW/DUP and emits INVOKESTATIC), gated to hosts >= 1.20.5 where those constructors were actually removed (on 1.20.1-1.20.4 the ctor still exists, and firing it there was itself the corruption). One fix clears the same VerifyError across three reports. Validated in-game on Forge 1.20.1: Wyrms of Nyrus advanced past the VerifyError (zero VerifyErrors in the crash).
  • 1.12.2 removed registration/model events bridged so the mod loads (#134). A 1.12.2 mod's @SubscribeEvent handler taking RegistryEvent.Register (the pre-1.13 registration event) or ModelRegistryEvent (the pre-1.17 model-registration event) hit NoClassDefFoundError. Retromod now embeds RegistryEvent/$Register/$MissingMappings and ModelRegistryEvent as synthetic classes that are a valid subtype of the host's eventbus Event (a class on EventBus 6 and NeoForge, a marker interface on EventBus 7, resolved by probing the running host so the stub extends or implements accordingly). The event never fires on a modern host, so the registration-via-event stays inert, but the class loads and event-bus registration completes without throwing. Skipped on the offline CLI where the host eventbus can't be resolved (no worse than before). Validated in-game on Forge 1.20.1: Mob Control Wands advanced past RegistryEvent$Register.
  • NeoForge AddReloadListenerEvent bridged to 26.2's AddServerReloadListenersEvent (#139, Legendary Survival Overhaul). NeoForge renamed the event around 26.x and made addListener require a ResourceLocation/Identifier id. A 1.21.1 mod referencing the old event hit NoClassDefFoundError. Retromod class-redirects the event (fixing the crash and retargeting the @SubscribeEvent parameter, both are game-bus events) and bridges the old one-arg addListener(listener) call to the new two-arg form via a shim that synthesizes a deterministic id, so the reload listener actually registers.
  • 1.12.2 mods no longer die at class-load on removed Forge API types (#131/#132/#134/#135, load layer). A 1.12.2 Forge mod's class implements/references a Forge type that later Forge deleted (IWorldGenerator and IGuiHandler and IMessage/IMessageHandler in 1.13, IForgeRegistryEntry in 1.17), so the class can't even load (NoClassDefFoundError) on a modern Forge/NeoForge host. Forge_1_12_2_to_1_13_2 now embeds an empty stub interface per removed type and redirects the old name onto it, so those classes load. Scope: this fixes the reported class-load errors; the paired registration idioms (GameRegistry.registerWorldGenerator, NetworkRegistry.registerGuiHandler, SimpleNetworkWrapper) target other removed classes and stay inert until bridged, so a mod may surface a next error there. CreativeModeTab.<init>(String) (#133, a constructor-to-builder bridge) is a separate follow-up. Part of the ongoing 1.12.2 baseline (#79); the load layer is transform-tested, full in-game running still needs iteration against the real jars.
  • 1.12.2 mcmod.info with multiple mods now declares every modid (#115). A single 1.12.2 jar can list several mods in one mcmod.info (The Betweenlands ships thebetweenlands + mclib). The synthesized mods.toml previously kept only the first entry, so the extra modids were never declared, FML never registered them, and any class or registration keyed on the second modid failed. ForgeModTransformer.generateTomlFromMcmodInfo now JSON-parses the mcmod.info (both the array form and the {"modList":[...]} v2 wrapper) and emits a [[mods]] block plus its own dependency tables per entry, with a lenient regex fallback preserving the old single-mod behavior for an unparseable file. Because the JSON path decodes escapes into real quotes/newlines, the emitted version is validated against a [A-Za-z0-9._-] allowlist (falling back to 1.0.0) so a crafted mcmod.info can't break out of the quoted TOML value to inject keys or spoof another modid, and the parse degrades to the regex fallback on any Throwable (including a StackOverflowError from deeply-nested input).
  • Custom PlacementModifier.getPositions no longer mis-renamed to count (#114). The Fabric intermediary short-name method_14452 is ambiguous: verified against Fabric Yarn 1.18.2 through 1.21.4 it names BOTH PlacementModifier.getPositions(context, random, BlockPos) -> Stream (Mojang getPositions) AND a separate getCount(random, BlockPos) -> int (Mojang count). The name-only 1.21.4 intermediary-to-mojang.tsv kept just count, so a mod's getPositions override was renamed to count: it stopped overriding the abstract method and threw AbstractMethodError during chunk generation on 26.x (any mod with a custom placement modifier). IntermediaryToMojangMapper.applyTo now registers all four descriptor variants (two eras: 1.18.x java/util/Random, 1.19+ RandomSource) so the transformer's ambiguous-name fallback resolves each site by descriptor. The systemic follow-up (a descriptor-qualified harvest so the tsv itself carries ambiguity) is noted in code.
  • Removed Fabric v0 ClientTickCallback bridged to ClientTickEvents (#129 Chat Bubbles). net.fabricmc.fabric.api.event.client.ClientTickCallback (SAM tick(MinecraftClient), static EVENT) was deleted from Fabric API around 1.16, so a 1.15-era mod referencing it dies with NoClassDefFoundError while its entrypoint class is even being loaded. Retromod now embeds a synthetic ClientTickCallback interface (kept under com/retromod/... so it can't split-package with the loader module) that preserves the tick SAM and the EVENT field; EVENT is wired in <clinit> to an array-backed event whose invoker fires once per client tick via ClientTickEvents.END_CLIENT_TICK. Gated to 26.1+ (the SAM uses the Mojang Minecraft type). Implemented pending in-game verification.
  • AOT no longer aborts a whole mod on one un-recomputable class (#125 MineColonies, #127 The Flying Things). The AOT compiler's last-resort per-class path recomputes stack-map frames with COMPUTE_FRAMES, which can throw deep inside ASM (Frame.merge -> ArrayIndexOutOfBoundsException / NegativeArraySizeException) on a class whose frames can't be recomputed after our rewrites. That exception propagated out and failed the entire jar's AOT, dropping the mod back to un-transformed bytes. It now falls back per-class to the robust main (JIT) transformer (which has its own ship-original frame-corruption guard), then to the original bytes, and never lets one class take down the jar. This mirrors the snapshot.8 frame-corruption fallback that already protected the JIT path.