Advanced
Chain resolution
Section titled “Chain resolution”Remap chains are flattened automatically before anything is applied. If your files define A to B and B to C, then A resolves directly to C. This works across files, so a remap added this year composes cleanly with one added last year:
{ "remaps": [ { "source": "oldmod:silver_ingot", "target": "midmod:silver_ingot" } ]}{ "remaps": [ { "source": "midmod:silver_ingot", "target": "newmod:silver_ingot" } ]}After flattening, oldmod:silver_ingot maps straight to newmod:silver_ingot. You never pay for intermediate hops at runtime, and you can leave historical remap files in place as a record of your pack’s migrations.
Two guard rails:
- Circular chains are rejected.
AtoBandBtoAproduces aCircular remap detectederror in the log and the chain is dropped rather than applied. - Chains are capped at depth 10. Deeper chains than that are almost certainly a config mistake.
An entry that ends up mapping an ID to itself after flattening is silently dropped.
Custom numerical ID mappings
Section titled “Custom numerical ID mappings”The built-in flattening table covers vanilla pre-1.13 IDs. Mods from that era used their own numerical IDs, which no global table can know, since they were assigned per world. If you are migrating such a world, supply your own mappings in config/remapids/numerical_ids.json:
{ "blocks": { "1234:0": "somemod:machine_frame", "1235:2": "somemod:machine_casing" }, "items": { "5678": "somemod:wrench" }}Keys are id or id:metadata strings, values are the modern namespaced IDs. The file is merged on top of the built-in table and custom entries override built-in ones, so you can also correct a vanilla resolution if you need to.
How it works
Section titled “How it works”RemapIDs operates at several levels, picked per remap type.
Registry aliases (block, item, fluid, entity_type). The source ID is injected as an alias into the registry’s internal lookup maps, so any lookup of the source resolves to the target’s entry. On Forge, ForgeRegistry.getValue() lookups are redirected as well.
Block state remapping. Aliasing a block is not enough on its own, because the game tracks placed blocks by block state ID. RemapIDs remaps entries in the global block state registry so network serialization uses the target block’s state IDs, and compatible properties carry over: a remapped log keeps its axis, a remapped slab keeps waterlogged, as long as the target block has the same property.
JSON rewriting (recipe, loot_table). Datapack JSON is rewritten at load time, replacing item, block, and tag references before the game parses them. This is also where tag-to-item ingredient conversion happens.
Tag rewriting (tag). Tag loading is intercepted and entries are redirected as tags are built.
World migration. On Fabric and NeoForge, block state and item stack deserialization from NBT is intercepted, converting old IDs as chunks and inventories load. On Forge, MissingMappingsEvent handles the same job when the source mod has been removed.
Load timing and modded targets
Section titled “Load timing and modded targets”Remap files are read at startup, but registry aliases are finalized only after every mod has finished registering its content: on Forge and NeoForge that is after load completes, on Fabric when the registries freeze. This ordering is why targets from other mods, including IDs created by KubeJS scripts, work even though they do not exist yet when your remap files are first parsed.
A practical consequence: you may see a startup warning that a target was not found in <type> registry yet. That is informational. The remap is retried at freeze time, and only fails (with another warning) if the target still does not exist then. See Troubleshooting for the log message reference.