Loaders & versions

What differs between Fabric and NeoForge, and across Minecraft versions.

Apoli is maintained per loader (Fabric, NeoForge) and per Minecraft version (1.20.1, 1.21.1). The data-pack surface is the same everywhere — a power JSON works on any build. The Java API mostly matches too, but where it touches the loader or version-specific game code, it differs. This page flags what to watch.

Fabric vs NeoForge

The registries, PowerType, contexts, and codecs are identical. What differs is how you hook into the game:

ConcernFabricNeoForge
Entry pointModInitializer@Mod class + mod bus events
Game hooksmostly mixinsoften events (plus mixins)
NetworkingFabric networking APINeoForge payload registration
Rendering hooksmixinsrender events / mixins

For example, the phasing/overlay renderer is a Fabric mixin on the client, but a render event on NeoForge. Keep loader-specific code in a loader source set (or a compat-style split) and share the codecs and logic.

Sharing code across loaders

The bulk of an addon — config records, codecs, the run/test/lifecycle logic — is loader-agnostic and belongs in shared code. Only the thin hook layer (entrypoint, event/mixin wiring, packet send/receive) needs a per-loader implementation. Apoli itself is organized this way; mirror it.

Minecraft 1.20.1 vs 1.21.1

Version gaps are mostly vanilla API changes that ripple through:

Area1.20.11.21.1
Item dataNBT tagdata components
Identifiersnew ResourceLocation(ns, path)ResourceLocation.fromNamespaceAndPath(...)
Particles from JSONfromCommand parsingobject codec
Some registriesdirectHolder / registry entries

These affect a data type’s codec (e.g. item stack components vs tag) and any code naming vanilla classes. When you port a page or a class between versions, the identifiers change flavor — don’t mix them.

Keep the versions in lockstep

Apoli and Origins are bumped together, and an addon should depend on a specific qualified build. Two rules that prevent the most common crash:

  1. Rebuild against the exact Apoli build you ship with. A “same version, different jar” mismatch crashes on join — see networking.
  2. When Apoli changes, republish it to mavenLocal and rebuild your addon with --refresh-dependencies so you’re not compiling against a stale copy.