1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-10-25 10:57:57 +00:00

Merge branch 'mc-1.20.x' into mc-1.21.x

This commit is contained in:
Jonathan Coates
2024-11-15 09:25:10 +00:00
111 changed files with 3691 additions and 1147 deletions

View File

@@ -11,12 +11,12 @@ package dan200.computercraft.api.lua;
* the given type, rather than requiring an exact type.
*
* <h2>Example:</h2>
* <pre>{@code
* {@snippet lang="java" :
* @LuaFunction
* public final void doSomething(Coerced<String> myString) {
* var value = myString.value();
* }
* }</pre>
* }
*
* @param value The argument value.
* @param <T> The type of the underlying value.

View File

@@ -21,16 +21,16 @@ import dan200.computercraft.api.peripheral.IPeripheral;
* <p>
* For example, the main CC: Tweaked mod defines a generic source for inventories, which works on {@code IItemHandler}s:
*
* <pre>{@code
* {@snippet lang="java" :
* public class InventoryMethods implements GenericSource {
* \@LuaFunction( mainThread = true )
* @LuaFunction(mainThread = true)
* public int size(IItemHandler inventory) {
* return inventory.getSlots();
* }
*
* // ...
* }
* }</pre>
* }
* <p>
* New capabilities or block lookups (those not built into Forge/Fabric) must be explicitly registered using the
* loader-specific API.

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

@@ -0,0 +1,129 @@
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.api.peripheral;
import javax.annotation.Nullable;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Consumer;
/**
* A thread-safe collection of computers.
* <p>
* This collection is intended to be used by peripherals that need to maintain a set of all attached computers.
* <p>
* It is recommended to use over Java's built-in concurrent collections (e.g. {@link CopyOnWriteArraySet} or
* {@link ConcurrentHashMap}), as {@link AttachedComputerSet} ensures that computers cannot be accessed after they are
* detached, guaranteeing that {@link NotAttachedException}s will not be thrown.
* <p>
* To ensure this, {@link AttachedComputerSet} is not directly iterable, as we cannot ensure that computers are not
* detached while the iterator is running (and so trying to use the computer would error). Instead, computers should be
* looped over using {@link #forEach(Consumer)}.
*
* <h2>Example</h2>
*
* {@snippet lang="java" :
* public class MyPeripheral implements IPeripheral {
* private final AttachedComputerSet computers = new ComputerCollection();
*
* @Override
* public void attach(IComputerAccess computer) {
* computers.add(computer);
* }
*
* @Override
* public void detach(IComputerAccess computer) {
* computers.remove(computer);
* }
* }
* }
*
* @see IComputerAccess
* @see IPeripheral#attach(IComputerAccess)
* @see IPeripheral#detach(IComputerAccess)
*/
public final class AttachedComputerSet {
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private final Set<IComputerAccess> computers = new HashSet<>(0);
/**
* Add a computer to this collection of computers. This should be called from
* {@link IPeripheral#attach(IComputerAccess)}.
*
* @param computer The computer to add.
*/
public void add(IComputerAccess computer) {
lock.writeLock().lock();
try {
computers.add(computer);
} finally {
lock.writeLock().unlock();
}
}
/**
* Remove a computer from this collection of computers. This should be called from
* {@link IPeripheral#detach(IComputerAccess)}.
*
* @param computer The computer to remove.
*/
public void remove(IComputerAccess computer) {
lock.writeLock().lock();
try {
computers.remove(computer);
} finally {
lock.writeLock().unlock();
}
}
/**
* Apply an action to each computer in this collection.
*
* @param action The action to apply.
*/
public void forEach(Consumer<? super IComputerAccess> action) {
lock.readLock().lock();
try {
computers.forEach(action);
} finally {
lock.readLock().unlock();
}
}
/**
* {@linkplain IComputerAccess#queueEvent(String, Object...) Queue an event} on all computers.
*
* @param event The name of the event to queue.
* @param arguments The arguments for this event.
* @see IComputerAccess#queueEvent(String, Object...)
*/
public void queueEvent(String event, @Nullable Object... arguments) {
forEach(c -> c.queueEvent(event, arguments));
}
/**
* Determine if this collection contains any computers.
* <p>
* This method is primarily intended for presentation purposes (such as rendering an icon in the UI if a computer
* is attached to your peripheral). Due to the multi-threaded nature of peripherals, it is not recommended to guard
* any logic behind this check.
* <p>
* For instance, {@code if(computers.hasComputers()) computers.queueEvent("foo");} contains a race condition, as
* there's no guarantee that any computers are still attached within the body of the if statement.
*
* @return Whether this collection is non-empty.
*/
public boolean hasComputers() {
lock.readLock().lock();
try {
return !computers.isEmpty();
} finally {
lock.readLock().unlock();
}
}
}

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();
@@ -48,8 +58,9 @@ public interface IPeripheral {
* {@code peripheral.call()}. This method can be used to keep track of which computers are attached to the
* peripheral, or to take action when attachment occurs.
* <p>
* Be aware that will be called from both the server thread and ComputerCraft Lua thread, and so must be thread-safe
* and reentrant.
* Be aware that may be called from both the server thread and ComputerCraft Lua thread, and so must be thread-safe
* and reentrant. If you need to store a list of attached computers, it is recommended you use a
* {@link AttachedComputerSet}.
*
* @param computer The interface to the computer that is being attached. Remember that multiple computers can be
* attached to a peripheral at once.
@@ -68,8 +79,9 @@ public interface IPeripheral {
* This method can be used to keep track of which computers are attached to the peripheral, or to take action when
* detachment occurs.
* <p>
* Be aware that this will be called from both the server and ComputerCraft Lua thread, and must be thread-safe
* and reentrant.
* Be aware that this may be called from both the server and ComputerCraft Lua thread, and must be thread-safe
* and reentrant. If you need to store a list of attached computers, it is recommended you use a
* {@link AttachedComputerSet}.
*
* @param computer The interface to the computer that is being detached. Remember that multiple computers can be
* attached to a peripheral at once.
@@ -79,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
@@ -93,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,155 @@
// 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.
* <p>
* {@snippet lang="java" :
* 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;
* }
* }
* }
* <p>
* Finally, we need to register our peripheral, so that ComputerCraft is aware of it:
* <p>
* {@snippet lang="java" :
* import dan200.computercraft.api.ComputerCraftAPI;
*
* public class ComputerCraftCompat {
* public static void register() {
* ComputerCraftAPI.registerGenericSource(new FurnacePeripheral());
* }
* }
* }
*
* <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}.
* <p>
* {@snippet lang="java" :
* 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;
* }
* }
* }
* <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 using the capability API, via {@code PeripheralCapability}.
*
* {@snippet lang="java" :
* import dan200.computercraft.api.peripheral.PeripheralCapability;
* import net.minecraft.world.level.block.entity.BlockEntityType;
* import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent;
*
* public class ComputerCraftCompat {
* public static void register(RegisterCapabilitiesEvent event) {
* event.registerBlockEntity(PeripheralCapability.get(), BlockEntityType.FURNACE, (f, s) -> new FurnacePeripheral(f));
* event.registerBlockEntity(PeripheralCapability.get(), BlockEntityType.BLAST_FURNACE, (f, s) -> new FurnacePeripheral(f));
* event.registerBlockEntity(PeripheralCapability.get(), BlockEntityType.SMOKER, (f, s) -> new FurnacePeripheral(f));
* }
* }
* }
*
* <h4>Registering {@code IPeripheral} on Fabric</h4>
* Registering a peripheral on Fabric can be done using the block lookup API, via {@code PeripheralLookup}.
*
* {@snippet lang="java" :
* 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);
* }
* }
* }
*/
package dan200.computercraft.api.peripheral;