StationAPI interop

What changes when StationAPI is installed, what does not, and how one jar ships for both worlds.

StationAPI is the older and larger content API for b1.7.3, and plenty of mods, and players, already run it. RetroAPI is not a replacement and does not ask you to choose: install both and they divide the work.

Who owns what

ConcernAloneWith StationAPI
Block & item registrationRetroAPIStationAPI (RetroAPI forwards your calls to it)
Ids & world storageRetroAPI's sidecarStationAPI's flattened format
Texture atlasRetroAPI's expanded atlasStationAPI's atlas
RecipesRetroAPIforwarded to StationAPI's registry
TagsRetroAPI (live, mutable)read from StationAPI's layouts too
Entities & spawn packetsRetroAPI, in both modes (the two systems are orthogonal)
Dimensions & portalsRetroAPIRetroAPI, forwarded into StationAPI's dimension registry
Components, particles, world features, multiblocks, sounds bridgeRetroAPI, in both modes

What you actually have to do

Nothing, if you registered through the retroapi entrypoint. That is the reason it exists: it is the one hook that fires identically in both modes, whereas RetroAPI's own registration events deliberately do not fire when StationAPI is present (StationAPI owns registration there, and firing both would double-register).

works the same with and without StationAPI
public class ExampleMod implements RetroModInitializer {
    @Override
    public void initRetro() {
        RUBY_ORE = RetroBlockAccess.create(Material.STONE)
            .strength(3.0F).texture(id("ruby_ore")).register(id("ruby_ore"));

        RetroFeatures.ore(RUBY_ORE).size(6).count(8).heightRange(4, 32).register();
    }
}

If your mod registers content from a BlockRegistrationCallback / ItemRegistrationCallback listener, it will register when RetroAPI is alone and silently not when StationAPI is present. Move that work into initRetro(); the events remain for reacting to registration, not for doing it.

Why some things stay RetroAPI's in both modes

They do not overlap with anything StationAPI provides, so there is nothing to hand over:

  • Entities use RetroAPI's own spawn interfaces and channels; the entity sets are disjoint and the wire formats do not collide.
  • Dimensions are registered by RetroAPI and then forwarded into StationAPI's dimension registry, reusing the same serial id so the DIM<n> folder stays put.
  • World features hook chunk decoration on the vanilla chunk cache, which StationAPI does not replace, so your ore generates in both modes, including in modded dimensions and custom generators.
  • Components, particles, the sound and particle bridges, multiblocks, positions are pure additions on top of vanilla behavior.

Shipping one jar

RetroAPI's own distribution nests a small compatibility mod that only loads when StationAPI is present, so a single download works either way. Your mod does not need an equivalent: depend on retroapi and let it detect its surroundings.

Two distribution builds exist for the two loaders:

JarLoaderNotes
retroapi-<version>.jarOrnitherequires OSL; the build the maven and this wiki point at
retroapi-<version>-babric.jarBabricself-contained: OSL is reverse-converted and bundled inside

Converting worlds

Because RetroAPI keeps an identifier → id map on disk even in StationAPI mode, a world can be moved between the two storage formats without losing modded content: the map is what tells the converter that 431 meant example_mod:ruby_ore. RetroAPI's test suite runs that round trip headlessly, vanilla → flattened → vanilla, and fails the build on any modded-data loss.

Known limitation of the round trip: content inside modded dimensions is not covered by the conversion assertions. The overworld path is.

Migrating a StationAPI mod

The APIs were kept deliberately close in shape, so most of a port is renaming:

StationAPIRetroAPI
@Entrypoint + event bus listenersRetroModInitializer.initRetro()
Identifier.of("mod:thing")NamespacedIdentifiers.from("mod", "thing")
BlockBuilder / template blocksRetroBlockAccess.create(...) / .of(Ctor::new)
BiomeBuilderBiomeBuilder (same shape, see Worldgen)
DimensionContainerRetroDimensions.register(id, factory)
Recipe register eventRetroRecipes, or the bridged callback