Skip to main content
Upgrades are items that can be inserted into machine upgrade slots to modify machine behavior. Each upgrade card targets a specific aspect of machine operation — speed, power efficiency, output, automation, and more.

Core interfaces and classes

Upgradeable

Any machine block entity that accepts upgrades must implement the Upgradeable interface:
public interface Upgradeable {
    ItemStackHandler getUpgradeInventory();
}
getUpgradeInventory() returns the ItemStackHandler representing the machine’s upgrade slots. The system checks for this interface before applying any upgrade; attempting to apply an upgrade to a non-upgradeable block throws an IllegalStateException.

UpgradeBase

UpgradeBase is the base class for all upgrade items. It extends BaseItem and holds:
  • A description string (shown in the item tooltip when the player holds Shift)
  • An hasBeenApplied flag tracking whether the upgrade is currently active
  • functionalUpgrade(BaseEntityBlock<?>, BlockState) — called to apply the upgrade’s effect; validates that the target block implements Upgradeable
  • appendHoverText(...) — shows gui.gm.press_shift until Shift is held, then shows the upgrade description
public class UpgradeBase extends BaseItem {
    public void functionalUpgrade(BaseEntityBlock<?> block, BlockState state) {
        if (block instanceof Upgradeable) {
            // apply effect
        } else throw new IllegalStateException("Cannot apply an upgrade to a non-upgradeable block.");
    }

    public boolean setApplied(boolean hasBeenApplied) { ... }
    public boolean hasBeenApplied() { ... }
    public String getEnglishDescription() { ... }
}
To create a custom upgrade item with no tooltip behavior, subclass UpgradeBase. UpgradeEmpty (used by the Upgrade Base placeholder) overrides appendHoverText with an empty body.

UpgradeMap

UpgradeMap is a record that pairs a DeferredBlockEntityType with an immutable list of compatible upgrade entries:
public record UpgradeMap<T extends BlockEntity>(
    DeferredBlockEntityType<T> entity,
    ImmutableList<Pair<ItemDefinition<UpgradeBase>, Integer>> upgrades
) {}
Each entry in upgrades is a Pair of an ItemDefinition<UpgradeBase> and an Integer representing the maximum number of that upgrade allowed in the machine. When no explicit maximum is provided during registration, it defaults to CoreUpgradeRegistry.MAX_UPGRADES (currently 8).

DeferredUpgradeMap

DeferredUpgradeMap wraps a DeferredHolder<UpgradeMap<?>, UpgradeMap<T>> to support deferred registration, which is required by NeoForge’s registry system. It also stores the target BasePoweredBlockEntity class for runtime validation.
public record DeferredUpgradeMap<T extends BasePoweredBlockEntity>(
    Class<? extends BasePoweredBlockEntity> entity,
    DeferredHolder<UpgradeMap<?>, UpgradeMap<T>> holder
) implements Supplier<UpgradeMap<T>> {
    @Override
    public UpgradeMap<T> get() { return holder.get(); }
}
Registration enforces that the target class implements Upgradeable at build time via a Preconditions.checkArgument guard.

Querying compatible upgrades

Use CoreUpgrades.getCompatibleUpgrades(Block) to retrieve the upgrade list for any registered machine block:
ImmutableList<Pair<ItemDefinition<UpgradeBase>, Integer>> upgrades =
    CoreUpgrades.getCompatibleUpgrades(myBlock);
The method resolves the block’s ResourceLocation, looks it up in CoreRegistries.UPGRADE_MAP_REGISTRY, and returns the associated upgrade pairs. Returns an empty ImmutableList if the block has no registered upgrades.

UpgradeFunctionBuilder and UpgradeFunction

Upgrades with active effects (as opposed to passive stat modifiers) use the UpgradeFunction interface:
public interface UpgradeFunction<T extends UpgradeBase, I> {
    void create(T base, I valueType);
    void work(I attribute);
}
  • create — called once when the upgrade is installed; receives the upgrade item instance and the attribute it targets
  • work — called each tick (or operation cycle) while the upgrade is active
UpgradeFunctionBuilder is a @FunctionalInterface that produces an UpgradeFunction:
@FunctionalInterface
public interface UpgradeFunctionBuilder<T extends UpgradeBase, I> {
    UpgradeFunction<T, I> build();
}
Pass a builder as the fourth argument to CoreUpgrades.create(...) to register an active function alongside the upgrade item. If null is passed, the upgrade is treated as a passive card with no tick logic. Example — Speed Upgrade implementation:
public class SpeedUpgrade implements CoreUpgrades.UpgradeFunction<UpgradeBase, Attribute.FloatValue> {
    @Override
    public void create(UpgradeBase base, Attribute.FloatValue valueType) {
        // initialization logic
    }

    @Override
    public void work(Attribute.FloatValue attribute) {
        // per-tick speed modification
    }
}
The Speed Upgrade is registered with its builder:
public static final ItemDefinition<UpgradeBase> SPEED =
    create("Speed Upgrade", "Increases machine operation speed.", UpgradeBase::new, SpeedUpgrade::new);

Effect stacking

Multiple copies of the same upgrade can be inserted up to the per-upgrade maximum defined in the machine’s UpgradeMap. The global cap per upgrade type per machine is CoreUpgradeRegistry.MAX_UPGRADES (8). Some upgrades set a lower maximum of 1 — for example, OVERCLOCK, AUTO_EJECTOR, SILENCING_COIL, REDSTONE_INTERFACE, and VOID_MOD on the Matter Fabricator. Inserting more copies than the registered maximum has no additional effect.

Installing an upgrade

1

Craft the upgrade card

Obtain the desired upgrade item. All upgrade cards appear in the General Mechanics creative tab.
2

Open the machine GUI

Right-click the machine block to open its interface.
3

Insert the upgrade card

Place the upgrade card into one of the machine’s upgrade slots. The upgrade takes effect immediately.
4

Verify compatibility

If the slot rejects the card, the upgrade is not compatible with that machine. Check the upgrade’s entry in the Upgrade Reference for supported machines.

Adding upgrade support to a machine

To register upgrade compatibility for a new machine:
  1. Implement Upgradeable on the machine’s BlockEntity class and return a valid ItemStackHandler from getUpgradeInventory().
  2. Add an entry to CoreUpgradeRegistry.UpgradePairs (or build a custom ImmutableList) listing each compatible upgrade and its per-machine maximum.
  3. Register a DeferredUpgradeMap via CoreUpgradeRegistry.build(...), passing the machine’s BlockDefinition, entity class, DeferredBlockEntityType, and upgrade list.
public static final DeferredUpgradeMap<MyBlockEntity> MY_MACHINE_UPGRADES =
    build(
        CoreBlocks.MY_MACHINE,
        MyBlockEntity.class,
        CoreBlockEntities.MY_MACHINE,
        UpgradePairs.POWERED_CRAFTER.getUpgrades()
    );
The build method validates that MyBlockEntity implements Upgradeable and registers the map into CoreRegistries.UPGRADE_MAP_REGISTRY.

Build docs developers (and LLMs) love