1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-01-09 00:40:29 +00:00

Clarify docs around registering peripherals

This commit is contained in:
Jonathan Coates 2024-11-13 10:19:10 +00:00
parent 87ce41f251
commit bdffabc08e
No known key found for this signature in database
GPG Key ID: B9E431FF07C98D06
13 changed files with 270 additions and 22 deletions

View File

@ -8,6 +8,8 @@ plugins {
id("cc-tweaked.vanilla")
}
val mcVersion: String by extra
java {
withJavadocJar()
}
@ -17,8 +19,36 @@ dependencies {
}
tasks.javadoc {
title = "CC: Tweaked $version Minecraft $mcVersion"
include("dan200/computercraft/api/**/*.java")
options {
(this as StandardJavadocDocletOptions)
groups = mapOf(
"Common" to listOf(
"dan200.computercraft.api",
"dan200.computercraft.api.lua",
"dan200.computercraft.api.peripheral",
),
"Upgrades" to listOf(
"dan200.computercraft.api.client.turtle",
"dan200.computercraft.api.pocket",
"dan200.computercraft.api.turtle",
"dan200.computercraft.api.upgrades",
),
)
addBooleanOption("-allow-script-in-comments", true)
bottom(
"""
<script src="https://cdn.jsdelivr.net/npm/prismjs@v1.29.0/components/prism-core.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/prismjs@v1.29.0/plugins/autoloader/prism-autoloader.min.js"></script>
<link href=" https://cdn.jsdelivr.net/npm/prismjs@1.29.0/themes/prism.min.css " rel="stylesheet">
""".trimIndent(),
)
}
// Include the core-api in our javadoc export. This is wrong, but it means we can export a single javadoc dump.
source(project(":core-api").sourceSets.main.map { it.allJava })
}

View File

@ -14,7 +14,7 @@ import dan200.computercraft.api.ComputerCraftAPI;
* as a proxy for all network objects. Whilst the node may change networks, an element's node should remain constant
* for its lifespan.
* <p>
* Elements are generally tied to a block or tile entity in world. In such as case, one should provide the
* Elements are generally tied to a block or block entity in world. In such as case, one should provide the
* {@link WiredElement} capability for the appropriate sides.
*/
public interface WiredElement extends WiredSender {

View File

@ -248,7 +248,7 @@ public class CommandAPI implements ILuaAPI {
* Get some basic information about a block.
* <p>
* The returned table contains the current name, metadata and block state (as
* with [`turtle.inspect`]). If there is a tile entity for that block, its NBT
* with [`turtle.inspect`]). If there is a block entity for that block, its NBT
* will also be returned.
*
* @param x The x position of the block to query.

View File

@ -15,7 +15,7 @@ import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType;
import java.util.Set;
/**
* A loot condition which checks if the tile entity has a non-0 ID.
* A loot condition which checks if the block entity has a computer ID.
*/
public final class HasComputerIdLootCondition implements LootItemCondition {
public static final HasComputerIdLootCondition INSTANCE = new HasComputerIdLootCondition();

View File

@ -23,7 +23,7 @@ import dan200.computercraft.api.peripheral.IPeripheral;
*
* <pre>{@code
* public class InventoryMethods implements GenericSource {
* \@LuaFunction( mainThread = true )
* \@LuaFunction(mainThread = true)
* public int size(IItemHandler inventory) {
* return inventory.getSlots();
* }

View File

@ -7,11 +7,13 @@
* <p>
* You probably want to start in the following places:
* <ul>
* <li>{@link dan200.computercraft.api.peripheral.IPeripheral} for registering new peripherals.</li>
* <li>{@link dan200.computercraft.api.peripheral} for registering new peripherals.</li>
* <li>
* {@link dan200.computercraft.api.lua.LuaFunction} and {@link dan200.computercraft.api.lua.IArguments} for
* adding methods to your peripheral or Lua objects.
* </li>
* <li>{@link dan200.computercraft.api.turtle.ITurtleUpgrade} for turtle upgrades.</li>
* <li>{@link dan200.computercraft.api.pocket.IPocketUpgrade} for pocket upgrades.</li>
* </ul>
*/
@DefaultQualifier(value = NonNull.class, locations = {

View File

@ -19,17 +19,17 @@ public interface GenericPeripheral extends GenericSource {
* Unlike normal {@link IPeripheral}s, {@link GenericPeripheral} do not have to have a type. By default, the
* resulting peripheral uses the resource name of the wrapped block entity (for instance {@code minecraft:chest}).
* <p>
* However, in some cases it may be more appropriate to specify a more readable name. Overriding this method allows
* you to do so.
* However, in some cases it may be more appropriate to specify a more readable name, or provide
* {@linkplain PeripheralType#getAdditionalTypes() additional types}. Overriding this method allows you to do so.
* <p>
* When multiple {@link GenericPeripheral}s return a non-empty peripheral type for a single tile entity, the
* lexicographically smallest will be chosen. In order to avoid this conflict, this method should only be
* implemented when your peripheral targets a single tile entity <strong>AND</strong> it's likely that you're the
* only mod to do so. Similarly this should <strong>NOT</strong> be implemented when your methods target a
* capability or other interface (such as Forge's {@code IItemHandler}).
* When multiple {@link GenericPeripheral}s provide a {@linkplain PeripheralType#getPrimaryType() primary peripheral
* type} for a single block entity, the lexicographically smallest will be chosen. In order to avoid this conflict,
* primary types should only be used when your peripheral targets a single block entity <strong>AND</strong> it's
* likely that you're the only mod to do so.
*
* @return The type of this peripheral or {@link PeripheralType#untyped()}.
* @see IPeripheral#getType()
* @see IPeripheral#getAdditionalTypes()
*/
default PeripheralType getType() {
return PeripheralType.untyped();

View File

@ -10,7 +10,6 @@ import dan200.computercraft.api.lua.*;
* A peripheral whose methods are not known at runtime.
* <p>
* This behaves similarly to {@link IDynamicLuaObject}, though also accepting the current {@link IComputerAccess}.
* Generally one may use {@link LuaFunction} instead of implementing this interface.
*/
public interface IDynamicPeripheral extends IPeripheral {
/**

View File

@ -4,19 +4,28 @@
package dan200.computercraft.api.peripheral;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.LuaFunction;
import dan200.computercraft.api.lua.LuaTask;
import javax.annotation.Nullable;
import java.util.Set;
/**
* The interface that defines a peripheral.
* A peripheral is an external device that a computer can interact with.
* <p>
* In order to expose a peripheral for your block or block entity, you should either attach a capability (Forge) or
* use the block lookup API (Fabric). This interface <em>cannot</em> be implemented directly on the block entity.
* Peripherals can be supplied by both a block (or block entity), or from
* {@linkplain dan200.computercraft.api.turtle.ITurtleUpgrade#createPeripheral(dan200.computercraft.api.turtle.ITurtleAccess, dan200.computercraft.api.turtle.TurtleSide) turtle}
* or {@linkplain dan200.computercraft.api.pocket.IPocketUpgrade#createPeripheral(dan200.computercraft.api.pocket.IPocketAccess) pocket}
* upgrades.
* <p>
* Peripherals should provide a series of methods to the user, either using {@link LuaFunction} or by implementing
* {@link IDynamicPeripheral}.
* See the {@linkplain dan200.computercraft.api.peripheral package documentation} for more information on registering peripherals.
* <p>
* Peripherals should provide a series of methods to the user, typically by annotating Java methods with
* {@link LuaFunction}. Alternatively, {@link IDynamicPeripheral} may be used to provide a dynamic set of methods.
* Remember that peripheral methods are called on the <em>computer</em> thread, and so it is not safe to interact with
* the Minecraft world by default. One should use {@link LuaFunction#mainThread()} or
* {@link ILuaContext#executeMainThreadTask(LuaTask)} to run code on the main server thread.
*/
public interface IPeripheral {
/**
@ -24,6 +33,7 @@ public interface IPeripheral {
* This can be queried from lua by calling {@code peripheral.getType()}
*
* @return A string identifying the type of peripheral.
* @see PeripheralType#getPrimaryType()
*/
String getType();
@ -81,7 +91,7 @@ public interface IPeripheral {
}
/**
* Get the object that this peripheral provides methods for. This will generally be the tile entity
* Get the object that this peripheral provides methods for. This will generally be the block entity
* or block, but may be an inventory, entity, etc...
*
* @return The object this peripheral targets
@ -95,7 +105,7 @@ public interface IPeripheral {
* Determine whether this peripheral is equivalent to another one.
* <p>
* The minimal example should at least check whether they are the same object. However, you may wish to check if
* they point to the same block or tile entity.
* they point to the same block or block entity.
*
* @param other The peripheral to compare against. This may be {@code null}.
* @return Whether these peripherals are equivalent.

View File

@ -96,6 +96,7 @@ public final class PeripheralType {
* Get the name of this peripheral type. This may be {@code null}.
*
* @return The type of this peripheral.
* @see IPeripheral#getType()
*/
@Nullable
public String getPrimaryType() {
@ -107,6 +108,7 @@ public final class PeripheralType {
* a peripheral might have.
*
* @return All additional types.
* @see IPeripheral#getAdditionalTypes()
*/
public Set<String> getAdditionalTypes() {
return additionalTypes;

View File

@ -11,7 +11,7 @@ import java.util.concurrent.TimeUnit;
* Monitors "work" associated with a computer, keeping track of how much a computer has done, and ensuring every
* computer receives a fair share of any processing time.
* <p>
* This is primarily intended for work done by peripherals on the main thread (such as on a tile entity's tick), but
* This is primarily intended for work done by peripherals on the main thread (such as on a block entity's tick), but
* could be used for other purposes (such as complex computations done on another thread).
* <p>
* Before running a task, one should call {@link #canWork()} to determine if the computer is currently allowed to

View File

@ -0,0 +1,205 @@
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
/**
* Peripherals for blocks and upgrades.
* <p>
* 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 {@linkplain dan200.computercraft.api.turtle.ITurtleUpgrade#createPeripheral(dan200.computercraft.api.turtle.ITurtleAccess, dan200.computercraft.api.turtle.TurtleSide) turtle}
* or {@linkplain dan200.computercraft.api.pocket.IPocketUpgrade#createPeripheral(dan200.computercraft.api.pocket.IPocketAccess) pocket}
* upgrades.
*
* <h2>Creating peripherals for blocks</h2>
* 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.
* <p>
* There are currently two possible ways to define a peripheral in ComputerCraft:
* <ul>
* <li>
* <p>
* <strong>With a {@linkplain dan200.computercraft.api.peripheral.GenericPeripheral generic peripheral}:</strong>
* 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.
* <p>
* This is the recommended approach if you just want to add a couple of methods, and do not need any advanced
* functionality.
* </li>
* <li>
* <p>
* <strong>With an {@link dan200.computercraft.api.peripheral.IPeripheral}:</strong> If your peripheral needs
* more advanced behaviour, such as knowing which computers it is attached to, then you can use an
* {@link dan200.computercraft.api.peripheral.IPeripheral}.
* <p>
* These peripherals are currently <strong>NOT</strong> compatible with the generic peripheral system, so
* methods added by other mods (including CC's built-in inventory methods) will not be available.
* </li>
* </ul>
* <p>
* 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.
*
* <h3>Creating a generic peripheral</h3>
* First, we'll need to create a new {@code final} class, that implements {@link dan200.computercraft.api.peripheral.GenericPeripheral}.
* You'll need to implement {@link dan200.computercraft.api.peripheral.GenericPeripheral#id()}, which should just return
* some namespaced-string with your mod id.
* <p>
* 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 {@code AbstractFurnaceBlockEntity}. We then annotate this method with
* {@link dan200.computercraft.api.lua.LuaFunction} to expose it to computers.
*
* <pre class="language language-java">{@code
* import dan200.computercraft.api.lua.LuaFunction;
* import dan200.computercraft.api.peripheral.GenericPeripheral;
* import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity;
*
* public final class FurnacePeripheral implements GenericPeripheral {
* @Override
* public String id() {
* return "mymod:furnace";
* }
*
* @LuaFunction(mainThread = true)
* public int getBurnTime(AbstractFurnaceBlockEntity furnace) {
* return furnace.litTime;
* }
* }
* }</pre>
* <p>
* Finally, we need to register our peripheral, so that ComputerCraft is aware of it:
*
* <pre class="language language-java">{@code
* import dan200.computercraft.api.ComputerCraftAPI;
*
* public class ComputerCraftCompat {
* public static void register() {
* ComputerCraftAPI.registerGenericSource(new FurnacePeripheral());
* }
* }
* }</pre>
*
* <h3>Creating a {@code IPeripheral}</h3>
* First, we'll need to create a new class that implements {@link dan200.computercraft.api.peripheral.IPeripheral}. This
* requires a couple of boilerplate methods: one to get the type of the peripheral, and an equality function.
* <p>
* We can then start adding peripheral methods to our class. Each method should be {@code final}, and annotated with
* {@link dan200.computercraft.api.lua.LuaFunction}.
*
* <pre class="language language-java">{@code
* import dan200.computercraft.api.lua.LuaFunction;
* import dan200.computercraft.api.peripheral.IPeripheral;
* import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity;
* import org.jetbrains.annotations.Nullable;
*
* public class FurnacePeripheral implements IPeripheral {
* private final AbstractFurnaceBlockEntity furnace;
*
* public FurnacePeripheral(AbstractFurnaceBlockEntity furnace) {
* this.furnace = furnace;
* }
*
* @Override
* public String getType() {
* return "furnace";
* }
*
* @LuaFunction(mainThread = true)
* public final int getBurnTime() {
* return furnace.litTime;
* }
*
* @Override
* public boolean equals(@Nullable IPeripheral other) {
* return this == other || other instanceof FurnacePeripheral p && furnace == p.furnace;
* }
* }
* }</pre>
* <p>
* Finally, we'll need to register our peripheral. This is done with capabilities on Forge, or the block lookup API on
* Fabric.
*
* <h4>Registering {@code IPeripheral} on Forge</h4>
* Registering a peripheral on Forge can be done by attaching the {@link dan200.computercraft.api.peripheral.IPeripheral}
* to a block entity. Unfortunately, this requires quite a lot of boilerplate, due to the awkward nature of
* {@code 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:
*
* <pre class="language language-java">{@code
* import dan200.computercraft.api.peripheral.IPeripheral;
* import net.minecraft.core.Direction;
* import net.minecraft.resources.ResourceLocation;
* import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity;
* import net.minecraft.world.level.block.entity.BlockEntity;
* import net.minecraftforge.common.capabilities.Capability;
* import net.minecraftforge.common.capabilities.CapabilityManager;
* import net.minecraftforge.common.capabilities.CapabilityToken;
* import net.minecraftforge.common.capabilities.ICapabilityProvider;
* import net.minecraftforge.common.util.LazyOptional;
* import net.minecraftforge.event.AttachCapabilitiesEvent;
* import org.jetbrains.annotations.Nullable;
*
* import java.util.function.Function;
*
* public class ComputerCraftCompat {
* public static final Capability<IPeripheral> CAPABILITY_PERIPHERAL = CapabilityManager.get(new CapabilityToken<>() {
* });
* private static final ResourceLocation PERIPHERAL = new ResourceLocation("mymod", "peripheral");
*
* public static void register(AttachCapabilitiesEvent<BlockEntity> event) {
* if (event.getObject() instanceof AbstractFurnaceBlockEntity furnace) {
* PeripheralProvider.attach(event, furnace, FurnacePeripheral::new);
* }
* }
*
* // A {@link ICapabilityProvider} that lazily creates an {@link IPeripheral} when required.
* private static 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();
* }
* }
* }
* }</pre>
*
* <h4>Registering {@code IPeripheral} on Fabric</h4>
* Registering a peripheral on Fabric can be done using the block lookup API, via {@code PeripheralLookup}.
*
* <pre class="language language-java">{@code
* import dan200.computercraft.api.peripheral.PeripheralLookup;
* import dan200.computercraft.example.FurnacePeripheral;
* import net.minecraft.world.level.block.entity.BlockEntityType;
*
* public class ComputerCraftCompat {
* public static void register() {
* PeripheralLookup.get().registerForBlockEntity((f, s) -> new FurnacePeripheral(f), BlockEntityType.FURNACE);
* PeripheralLookup.get().registerForBlockEntity((f, s) -> new FurnacePeripheral(f), BlockEntityType.BLAST_FURNACE);
* PeripheralLookup.get().registerForBlockEntity((f, s) -> new FurnacePeripheral(f), BlockEntityType.SMOKER);
* }
* }
* }</pre>
*/
package dan200.computercraft.api.peripheral;

View File

@ -30,7 +30,7 @@ import java.util.concurrent.TimeUnit;
* <p>
* Now, if anywhere during this period, we use more than our allocated time slice, the executor is marked as
* {@link State#HOT}. This means it will no longer be able to execute {@link MainThread} tasks (though will still
* execute tile entity tasks, in order to prevent the main thread from exhausting work every tick).
* execute block entity tasks, in order to prevent the main thread from exhausting work every tick).
* <p>
* At the beginning of the next tick, we increment the budget e by {@link MainThreadConfig#maxComputerTime()} and any
* {@link State#HOT} executors are marked as {@link State#COOLING}. They will remain cooling until their budget is fully