Package dan200.computercraft.api.peripheral


package dan200.computercraft.api.peripheral
Peripherals for blocks and upgrades.

A peripheral is an external device that a computer can interact with. Peripherals can be supplied by both a block (or block entity), or from turtle or pocket upgrades.

Creating peripherals for blocks

One of the most common things you'll want to do with ComputerCraft's API is register new peripherals. This is relatively simple once you know how to do it, but may be a bit confusing the first time round.

There are currently two possible ways to define a peripheral in ComputerCraft:

  • With a generic peripheral: Generic peripherals are a way to add peripheral methods to any block entity, in a trait-based manner. This allows multiple mods to add methods to the same block entity.

    This is the recommended approach if you just want to add a couple of methods, and do not need any advanced functionality.

  • With an IPeripheral: If your peripheral needs more advanced behaviour, such as knowing which computers it is attached to, then you can use an IPeripheral.

    These peripherals are currently NOT compatible with the generic peripheral system, so methods added by other mods (including CC's built-in inventory methods) will not be available.

In the following examples, we'll write a peripheral method that returns the remaining burn time of a furnace, and demonstrate how to register this peripheral.

Creating a generic peripheral

First, we'll need to create a new final class, that implements GenericPeripheral. You'll need to implement GenericSource.id(), which should just return some namespaced-string with your mod id.

Then, we can start adding methods to your block entity. Each method should take its target type as the first argument, which in this case is a AbstractFurnaceBlockEntity. We then annotate this method with LuaFunction to expose it to computers.

public class FurnacePeripheral implements GenericPeripheral {
    @Override
    public String id() {
        return new ResourceLocation(ExampleMod.MOD_ID, "furnace").toString();
    }

    @LuaFunction(mainThread = true)
    public int getBurnTime(AbstractFurnaceBlockEntity furnace) {
        // Don't do it this way! Use an access widener/transformer to access the "litTime" field instead.
        return furnace.saveWithoutMetadata().getInt("BurnTime");
    }
}
Finally, we need to register our peripheral, so that ComputerCraft is aware of it:
ComputerCraftAPI.registerGenericSource(new FurnacePeripheral());

Creating a IPeripheral

First, we'll need to create a new class that implements IPeripheral. This requires a couple of boilerplate methods: one to get the type of the peripheral, and an equality function.

We can then start adding peripheral methods to our class. Each method should be final, and annotated with LuaFunction.

public class BrewingStandPeripheral implements IPeripheral {
    private final BrewingStandBlockEntity brewingStand;

    public BrewingStandPeripheral(BrewingStandBlockEntity brewingStand) {
        this.brewingStand = brewingStand;
    }

    @Override
    public String getType() {
        return "brewing_stand";
    }

    @LuaFunction
    public final int getFuel() {
        // Don't do it this way! Use an access widener/transformer to access the "fuel" field instead.
        return brewingStand.saveWithoutMetadata().getInt("Fuel");
    }

    @Override
    public boolean equals(@Nullable IPeripheral other) {
        return other instanceof BrewingStandPeripheral o && brewingStand == o.brewingStand;
    }
}
Finally, we'll need to register our peripheral. This is done with capabilities on Forge, or the block lookup API on Fabric.

Registering IPeripheral on Forge

Registering a peripheral on Forge can be done by attaching the IPeripheral to a block entity. Unfortunately, this requires quite a lot of boilerplate, due to the awkward nature of ICapabilityProvider. If you've got an existing system for dealing with this, we recommend you use that, otherwise you can use something similar to the code below:
// The main function to attach peripherals to block entities. This should be added to the Forge event bus.
public static void attachPeripherals(AttachCapabilitiesEvent<BlockEntity> event) {
    if (event.getObject() instanceof BrewingStandBlockEntity brewingStand) {
        PeripheralProvider.attach(event, brewingStand, BrewingStandPeripheral::new);
    }
}

// Boilerplate for adding a new capability provider

public static final Capability<IPeripheral> CAPABILITY_PERIPHERAL = CapabilityManager.get(new CapabilityToken<>() {
});
private static final ResourceLocation PERIPHERAL = new ResourceLocation(ExampleMod.MOD_ID, "peripheral");

// A {@link ICapabilityProvider} that lazily creates an {@link IPeripheral} when required.
private static final class PeripheralProvider<O extends BlockEntity> implements ICapabilityProvider {
    private final O blockEntity;
    private final Function<O, IPeripheral> factory;
    private @Nullable LazyOptional<IPeripheral> peripheral;

    private PeripheralProvider(O blockEntity, Function<O, IPeripheral> factory) {
        this.blockEntity = blockEntity;
        this.factory = factory;
    }

    private static <O extends BlockEntity> void attach(AttachCapabilitiesEvent<BlockEntity> event, O blockEntity, Function<O, IPeripheral> factory) {
        var provider = new PeripheralProvider<>(blockEntity, factory);
        event.addCapability(PERIPHERAL, provider);
        event.addListener(provider::invalidate);
    }

    private void invalidate() {
        if (peripheral != null) peripheral.invalidate();
        peripheral = null;
    }

    @Override
    public <T> LazyOptional<T> getCapability(Capability<T> capability, @Nullable Direction direction) {
        if (capability != CAPABILITY_PERIPHERAL) return LazyOptional.empty();
        if (blockEntity.isRemoved()) return LazyOptional.empty();

        var peripheral = this.peripheral;
        return (peripheral == null ? (this.peripheral = LazyOptional.of(() -> factory.apply(blockEntity))) : peripheral).cast();
    }
}

Registering IPeripheral on Fabric

Registering a peripheral on Fabric can be done using the block lookup API, via PeripheralLookup.
PeripheralLookup.get().registerForBlockEntity((f, s) -> new BrewingStandPeripheral(f), BlockEntityType.BREWING_STAND);