RetroAPI

A content library for Minecraft Beta 1.7.3: register blocks, items, mobs, dimensions and world features without hand-rolling id management, and without your mod corrupting the world it is installed in.

Where this sits

Beta 1.7.3 is the version a lot of people mean when they say they miss Minecraft. Quiet worlds, that particular green, terrain that goes up and then stops. It is also, by modern standards, a version with no modding support whatsoever: no registries, no data packs, no ids that survive a mod being removed.

Plenty of work has already gone into fixing that, and RetroAPI stands on it rather than replacing it:

PieceWhat it does
BabricA fork of Fabric's loader for b1.7.3 specifically. It made Fabric-style mods and mixins possible on this version years before RetroAPI existed, and it is still perfectly good, RetroAPI ships a babric build for exactly that reason.
OrnitheAnother Fabric fork, aimed at a wide sweep of old Minecraft versions rather than one, with mappings (biny/calamus) and Gradle tooling to match. Not the first to do this, just the broadest.
OSL (Ornithe Standard Libraries)Entrypoints, lifecycle events, networking. The plumbing under the loader.
StationAPIThe established content API for this version, and it predates RetroAPI by a long way. It flattens the world format and brings a modern registry with it. If you already use it, keep using it, RetroAPI is built to sit alongside it (see StationAPI interop).
RetroAPIThe library this wiki is about: a smaller, opinionated content layer that leaves the vanilla world format alone and puts modded data beside it.

What RetroAPI is actually for

Two things, and it is worth being blunt about them because everything else follows.

1. Making ordinary mod work short

Registering a block should be one call, not an id allocation scheme, an atlas patch and a save-format decision. Most of this wiki is that: a builder for the common case, an escape hatch for the rest.

the whole registration of a block
RUBY_ORE = RetroBlockAccess.create(Material.STONE)
    .strength(3.0F)
    .texture(id("ruby_ore"))
    .needsTool(RetroToolTier.IRON)
    .register(id("ruby_ore"));

2. Not eating the world

This is the part that matters more, and it is the reason the library has the shape it does.

Beta stores a block as a byte plus a 4-bit metadata nibble. There is no room in that format for a modded block, so every approach to modded content on this version has to answer the same question: where do the extra bits live? Answer it by writing modded ids into the vanilla arrays and you get worlds that are fine right up until the mod is removed or its ids shift, at which point chunks come back as the wrong blocks, or do not come back at all.

RetroAPI's answer is a sidecar. Vanilla's own bytes stay exactly as vanilla wrote them. Anything that does not fit, block ids ≥ 256, the state bits above the nibble, modded item data, lives in separate files next to the region data, keyed by a stable string identifier (mymod:ruby_ore), with a per-world id map translating those names to whatever numbers this session happens to be using. Numbers can move between launches; names cannot. Remove the mod and the vanilla half of the world still loads, because the vanilla half was never touched. Details in World safety.

That is also why RetroAPI can hand you 32,000 block slots, block states wider than the nibble, and per-stack data components on a version with none of those things: they are sidecar features, not edits to the format the vanilla game reads.

Code-driven or data-driven, your choice

Almost everything here can be declared two ways: as a call in your mod, or as a JSON file the game loads. Textures, block states, models, tags and translations all work either way, and they mix freely, one block can take its textures from code and its variants from a blockstate file.

This wiki shows both. Wherever the two differ, a switch sits directly above them, flip it and the whole site follows, and it remembers your choice:

in your mod's initRetro()
MACHINE = RetroBlockAccess.create(Material.STONE)
    .facing()
    .sided(id("machine_top"), id("machine_side"), id("machine_front"))
    .register(id("machine"));
assets/example_mod/models/block/machine.json
{
  "parent": "minecraft:block/orientable",
  "textures": {
    "top":   "example_mod:block/machine_top",
    "side":  "example_mod:block/machine_side",
    "front": "example_mod:block/machine_front"
  }
}
assets/example_mod/blockstates/lamp.json
{
  "properties": { "lit": ["false", "true"] },
  "variants": {
    "lit=false": { "model": "example_mod:block/lamp_off" },
    "lit=true":  { "model": "example_mod:block/lamp_on" }
  }
}

Neither is "the real way". Code is the default here because it is usually shorter, cannot mistype a file path, and keeps a block's definition in one place. Data files win when an artist or pack author needs to change something without touching Java, when the same shape repeats across dozens of blocks, or when you want a resource pack to be able to override it.

They are not always alternatives. Textures, names, tags and sounds genuinely are: pick a side. Block states are not. The blockstate JSON is the only place that can say what each variant looks like, and code is the only place that hands you a typed property object to read and write states with, so a block whose appearance changes with its state normally uses both halves. How registration works spells out which topics are which.

Start here

Two zips, one decision:

Then: set up a projectentrypointsthe registry.

Coming from modern Minecraft? Most of your instincts transfer: fabric.mod.json, entrypoints, mixins and Loom all behave the way you expect. Three things do not. Identifiers come from OSL (NamespacedIdentifiers.from("mod", "thing")). There is no integrated server, singleplayer is the client simulating the world itself (Entrypoints & sides). And the registry is a library's job here, not the game's, which is why ids move between sessions and why nothing you keep should be a raw number.

Already using StationAPI? Nothing here asks you to stop. RetroAPI detects it, hands registration and the atlas over to it, and keeps its own systems (entities, dimensions, components, particles, world features) working alongside. The differences are collected in StationAPI interop.