Retromod

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

File Details

Retromod 1.2.0-snapshot.8 (Forge 1.21.11)

  • R
  • Jul 4, 2026
  • 3.22 MB
  • 3
  • 1.21.11
  • Forge

File Name

retromod-1.2.0-snapshot.8+1.21.11.jar

Supported Versions

  • 1.21.11

Curse Maven Snippet

Forge

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

Learn more about Curse Maven

The three remaining 1.2.0 pillars landed, plus a full in-game acceptance pass for Apollo's Enchantment Rebalance on Fabric 26.2 (#119). §A the pre-26.1 Fabric acceptance pass: real 1.16.5 Serilum mods run clean through full server start on a 1.20.1 host (Text/Entity-field/Material bridges + a rebuilt constructor-to-factory pass). §D the 1.12.2 in-game layers (SRG member namespace, the pre-1.13 FML lifecycle idiom, a 1.20.1-target class-move table). §B the 26.2 render-API stand-ins (MultiBufferSource, Tesselator, VertexFormat$Mode). And #119: the anvil-crash phantom redirect, a systemic phantom-target safety net, and the blank-enchanted-books tooltip rename, all fixed and verified in-game.

Added

  • 26.2 render-API stand-ins (SS B): render-referencing mods now LOAD on 26.2. 26.2 deleted MultiBufferSource/MultiBufferSource$BufferSource and Tesselator, and replaced the VertexFormat$Mode enum with PrimitiveTopology (all verified against the real 26.1.2 and 26.2 client jars), so a 26.1-translated mod referencing any of them died NoClassDefFoundError at class load on 26.2, even when the render path never ran. New per-mod-embedded stand-ins reproduce the exact 26.1 shapes: BufferSource.getBuffer(renderType) returns a REAL 26.2 BufferBuilder built from the RenderType's own format()/primitiveTopology(), and Tesselator.getInstance().begin(...) works the same way, so mod geometry code actually executes; the endBatch family is staged (warns once and drops the batch: frame submission through the new SubmitNodeCollector.submitCustomGeometry pipeline is the follow-up). VertexFormat$Mode is class-redirected to PrimitiveTopology (identical constants and public fields). Registered in the 26.1 to 26.2 layer on all loaders. Tested (RenderBufferSyntheticsTest).
  • 1.12.2 SRG member namespace (SS D, #103/#108/#117). Distributed 1.12.2 Forge mods reference members by the OLD SRG names (func_NNNNN_x/field_NNNNN_x), a different namespace from the modern m_/f_ one, so the SRG remap never fired for them. A new bundled dictionary (srg-1.12.2-to-mojang.tsv, 7,736 entries) maps them to readable names: harvested from the MCPBot 1.12.2 stable CSVs and kept ONLY where the name exists in Mojang's official 1.20.1 mappings (a wrong rename is worse than none). The remapper's SRG branch now matches both eras, and the dictionary loader merges both files. Also fixed a latent core bug the new tests flushed out: an SRG-only registration built no remapper at all (masked before because real runs always carry class redirects). Tested (Srg1122MemberRemapTest).
  • 1.12.2 class moves for 1.20.x hosts (SS D). The 1.12.2 class-move table only existed with 26.1-target names, but the reported 1.12.2 hosts (#103/#108/#117) all run 1.20.1. A new validated variant (forge-1.12.2-class-moves-1201.tsv, 125 rows, every target checked against Mojang's official 1.20.1 mappings by scripts/harvest-1.12.2-class-moves-1201.py) loads on 1.20.x hosts instead, and additionally carries the families 26.1 removed but 1.20.x still has (ItemSword -> SwordItem, EnumAction -> UseAnim, CreativeTabs -> CreativeModeTab, tool/armor tiers, ...). The Block/Item Properties constructor bridge and the Material static-field nuller now apply on 1.20.x hosts too (verified: 1.20 already removed Material, and Properties.of()/Item$Properties() are present with the same shapes as 26.1). 1.21.x hosts get no table (none validated against those jars). Tested (Forge1122ClassMovesTest).
  • 1.12.2 in-game FML lifecycle layer (SS D, #103/#108/#117). A 1.12.2 mod's @Mod(modid=...) annotation shape is invisible to a modern loader (it reads @Mod(value)), and nothing ever calls its @Mod.EventHandler setup methods, so even a mod that LOADED did nothing. The upgrade pass rewrites the annotation to the modern shape and injects a constructor-time call into an embedded lifecycle bridge that constructs the legacy FMLPreInitialization/FMLInitialization/FMLPostInitialization event stand-ins (config-dir surface included: getSuggestedConfigurationFile yields config/<modid>.cfg) and reflectively invokes the mod's handlers in order. Active only when the 1.12.2 shim chain registered. Tested (Forge1122LifecycleTest, including a functional fire test).
  • Pre-26.1 Fabric bridge chain from the LIVE acceptance pass (SS A, #55). Real 1.16.5 Fabric content mods (Serilum's Double Doors, Mineral Chance, Stack Refill plus their shared Collective library) were run on an actual 1.20.1 Fabric server and every crash in the chain was fixed at the transform level:
    • Pre-1.19 Text bridge. The 1.19 chat rework removed the TranslatableText/LiteralText constructors; new TranslatableText("key") died NoSuchMethodError. Constructor-to-factory redirects target Text.translatable/Text.literal (plus the with-args overload), registered with the InterfaceMethodref form (Text is an interface on 1.19+: a plain-Methodref INVOKESTATIC dies IncompatibleClassChangeError), and both old classes class-redirect to MutableText so the mod's downstream fluent calls (formatted, setStyle, append: same intermediary ids on MutableText since 1.16, verified on the 1.20.1 jar) type-check against the factory's return value.
    • Pre-1.17 Entity field bridge. Entity.onGround went non-public in the 1.17 access cleanup; direct access died IllegalAccessError. GETFIELD/PUTFIELD rewrite to isOnGround()/setOnGround(boolean).
    • Pre-1.20 Material bridge. MC 1.20 deleted the Material system, and ONE List<Material> in a <clinit> (Collective's GlobalVariables) took the whole class down with NoClassDefFoundError. class_3614 class-redirects to a shipped MaterialPolyfill (44 distinct constants under their exact 1.16.5 intermediary field ids, harvested from the official intermediary mappings), and BlockState.getMaterial() devirtualizes to a staged fromState (returns a sentinel matching no constant, so material-membership features quietly no-op instead of crashing).
    • All three bridges are host-introspecting (probe with Class.forName(..., false, ...), never initialize): they register only where the host actually lost the API, so 1.16.x-1.18.x hosts are untouched. Tested (Pre1_17EntityFieldBridgeTest, Pre1_20MaterialBridgeTest, plus the real failing Collective class as a gated repro).
    • Also fixed while here: the fuzzy method resolver never activated on pre-26.1 Fabric hosts (its MC-jar probe only knew the Mojang-named SharedConstants; intermediary hosts need the class_310/MinecraftServer probes), and Fabric_1_16_5_to_1_17 carried four 26.x-only tick-event renames (*WorldTick to *LevelTick) that broke the very hosts the shim targets; the correct copies live in the 26.1 shim.

Fixed

  • 1.12.2 Forge mods no longer die "mods.toml missing metadata for modid null" on 1.20.1+ (#120, Betweenlands / Scape and Run Parasites). A 1.12.2 @Mod annotation is @Mod(modid="x", name=..., version=..., dependencies=...); modern Forge's @Mod has ONLY value() (which IS the modid), so it read value() = null from the old shape and aborted mod loading with a null modid. The Forge1122LifecycleSynthetics @Mod modernization (added this cycle) collapses the whole legacy annotation to @Mod(value=modid) so Forge reads a real id. Verified on the real SRPMain class from Scape and Run Parasites (@Mod(modid="srparasites", ...) -> @Mod(value="srparasites")). The runtime Forge path already activated the pass via the 1.12.2 shim chain; the CLI transform now activates it too (at startup, self-gating on the old annotation shape so it never touches modern mods), so pre-transformed jars get the same fix. Tested (Forge1122LifecycleTest, incl. the full 4-element annotation shape).
  • Custom ClientTooltipComponent renderers now draw on 26.1+ (Apollo's Enchantment Rebalance blank enchanted books, #119). 26.1 renamed ClientTooltipComponent.renderText/renderImage to extractText/extractImage (the params take GuiGraphics, itself renamed to GuiGraphicsExtractor). The intermediary->Mojang table is 1.21.4-based and mapped those two intermediary ids (method_32665/method_32666) to the OLD names, so a distributed 1.21.x Fabric mod's overrides were renamed to renderText/renderImage and no longer overrode the interface: the vanilla empty default methods ran, and the mod's custom tooltip drew nothing (AER's enchanted books showed no enchantment lines, even though the enchantments themselves applied and functioned). Corrected the two id mappings to the 26.1 names; the fix is id-scoped, so the four unrelated renderText methods are untouched. Tested (ClientTooltipRenderRenameTest, both the mapping and a transformed override). This is one instance of a broader class: the 1.21.4-based intermediary table carries stale names for any method renamed between 1.21.4 and 26.1, which breaks overrides on a 26.1+ host; more such renames may need the same correction as they surface.
  • Apollo's Enchantment Rebalance no longer crashes the anvil on Fabric 26.2 (#119). The mod's anvil createResult calls Enchantment.getMaxLevel(), which Retromod was rewriting to a com/retromod/shim/*/embedded/EnchantmentShim that was never written (a phantom target): the anvil combined an item + book, the rewritten call resolved, and it died NoClassDefFoundError on first use. Three compounding faults, all fixed:
    • The Fabric transform path had no loader-type filter. RetromodPreLaunch registered EVERY version shim regardless of getModLoaderType(), unlike the other three entry points. So a NeoForge shim's Mojang-named redirect (NeoForge_1_20_6_to_1_21) fired on a Fabric mod once the intermediary->Mojang harvest made the names match. It now applies the same {fabric, common} filter the NeoForge/Forge/onInitialize paths already use, which closes the entire class of cross-loader phantom bites on Fabric (NeoForge/Forge enchantment, ItemStack-NBT, DamageSource, Material, Tag redirects, etc.).
    • The redirects were wrong regardless. Enchantment.getMaxLevel()/getMinLevel() never left the API (present 1.21 through 26.2, backed by the data-driven definition); redirecting them was pointless. Both the NeoForge and the Yarn-keyed Fabric copies (dead code per the intermediary-namespace note) are deleted. getRarity was genuinely removed but can't be bridged by a call-site redirect alone (its Rarity return type is gone too), so it's deleted pending a proper synthetic bridge.
    • A phantom-target safety net. The transformer now sweeps its redirect tables once, before the first transform, and drops any redirect whose com/retromod/* target is neither a registered synthetic nor a loadable class, logging one warning instead of letting it detonate at first use. This retroactively neutralizes several other never-written targets found across the shim tree (FlatteningShim, SidedProxyShim, a wrong-package GameRegistryShim, ...): the affected feature quietly no-ops instead of crashing whatever runs it. This also surfaced and fixed a latent bug where a redirect set containing only super-constructor + class redirects skipped the method-body visitor entirely (the 1.12.2 Block/Item super(Material)->super(Properties) bridge relied on an unrelated method redirect being present to stay active). The reported blank-enchanted-books symptom is a separate client mixin-apply matter still under investigation.
  • Constructor-to-factory rewriting no longer corrupts stack frames when the args contain branches (found live on Collective's DuskConfig). The old deferral buffered ONE pending NEW and flushed it as soon as a nested plain new appeared, i.e. mid-expression: with a ternary in the constructor args the buffered NEW+DUP landed on one branch path only, ASM's frame computation failed (Frame.merge AIOOBE), and the silent COMPUTE_MAXS fallback then shipped the class with its ORIGINAL StackMapTable whose offsets the rewrite had invalidated: ClassFormatError: StackMapTable format error: bad offset for Uninitialized at load, taking the whole mod down. The machinery is now a proper deferral STACK flushed only at the <init> convergence point (branch-safe by construction, and nested redirect-eligible news compose), it moved UPSTREAM of the class remapper into its own pass (CtorRedirectPrePass, so constructor redirects stay keyed on distinct pre-remap owners even when several old classes retype onto ONE new class, as the Text bridge needs), the fallback now logs the primary failure instead of swallowing it, and a fallback pass that made frame-invalidating rewrites refuses to ship the result (degrades to the untransformed class instead of a corrupt one). Regression-tested with synthesized pathological shapes (CtorRedirectBranchedArgsTest) plus the real failing class.