mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2024-12-25 17:40:30 +00:00
Unify the generic peirpheral system a litte
Allows registering arbitrary block lookup functions instead of a platform-specific capability. This is roughly what Fabric did before, but generalised to also take an invalidation callback. This callback is a little nasty - it needs to be a NonNullableConsumer on Forge, but that class isn't available on Fabric. For now, we make the lookup function (and thus the generic peripheral provider) generic on some <T extends Runnable> type, then specialise that on the Forge side. Hopefully we can clean this up when NeoForge reworks capabilities.
This commit is contained in:
parent
1747c74770
commit
0ff58cdc3e
@ -0,0 +1,36 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.shared.peripheral.generic;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract some component (for instance a capability on Forge, or a {@code BlockApiLookup} on Fabric) from a block and
|
||||||
|
* block entity.
|
||||||
|
*
|
||||||
|
* @param <C> A platform-specific type, used for the invalidation callback.
|
||||||
|
*/
|
||||||
|
public interface ComponentLookup<C extends Runnable> {
|
||||||
|
/**
|
||||||
|
* Extract some component from a block in the world.
|
||||||
|
*
|
||||||
|
* @param level The current level.
|
||||||
|
* @param pos The position of the block in the level.
|
||||||
|
* @param state The block state at that position.
|
||||||
|
* @param blockEntity The block entity at that position.
|
||||||
|
* @param side The side of the block to extract the component from. Implementations should try to use a
|
||||||
|
* sideless lookup first, but may fall back to a sided lookup if needed.
|
||||||
|
* @param invalidate An invalidation function to call if this component changes.
|
||||||
|
* @return The found component, or {@code null} if not present.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
Object find(Level level, BlockPos pos, BlockState state, BlockEntity blockEntity, Direction side, C invalidate);
|
||||||
|
}
|
@ -6,12 +6,9 @@ package dan200.computercraft.shared.peripheral.generic;
|
|||||||
|
|
||||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
import dan200.computercraft.api.peripheral.PeripheralType;
|
import dan200.computercraft.api.peripheral.PeripheralType;
|
||||||
import dan200.computercraft.core.methods.MethodSupplier;
|
|
||||||
import dan200.computercraft.core.methods.NamedMethod;
|
import dan200.computercraft.core.methods.NamedMethod;
|
||||||
import dan200.computercraft.core.methods.PeripheralMethod;
|
import dan200.computercraft.core.methods.PeripheralMethod;
|
||||||
import dan200.computercraft.shared.computer.core.ServerContext;
|
|
||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
import net.minecraft.server.MinecraftServer;
|
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@ -28,16 +25,10 @@ import java.util.Set;
|
|||||||
* See the platform-specific peripheral providers for the usage of this.
|
* See the platform-specific peripheral providers for the usage of this.
|
||||||
*/
|
*/
|
||||||
final class GenericPeripheralBuilder {
|
final class GenericPeripheralBuilder {
|
||||||
private final MethodSupplier<PeripheralMethod> peripheralMethods;
|
|
||||||
|
|
||||||
private @Nullable String name;
|
private @Nullable String name;
|
||||||
private final Set<String> additionalTypes = new HashSet<>(0);
|
private final Set<String> additionalTypes = new HashSet<>(0);
|
||||||
private final ArrayList<SaturatedMethod> methods = new ArrayList<>();
|
private final ArrayList<SaturatedMethod> methods = new ArrayList<>();
|
||||||
|
|
||||||
GenericPeripheralBuilder(MinecraftServer server) {
|
|
||||||
peripheralMethods = ServerContext.get(server).peripheralMethods();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
IPeripheral toPeripheral(BlockEntity blockEntity, Direction side) {
|
IPeripheral toPeripheral(BlockEntity blockEntity, Direction side) {
|
||||||
if (methods.isEmpty()) return null;
|
if (methods.isEmpty()) return null;
|
||||||
@ -46,18 +37,16 @@ final class GenericPeripheralBuilder {
|
|||||||
return new GenericPeripheral(blockEntity, side, name, additionalTypes, methods);
|
return new GenericPeripheral(blockEntity, side, name, additionalTypes, methods);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean addMethods(Object target) {
|
void addMethod(Object target, String name, PeripheralMethod method, @Nullable NamedMethod<PeripheralMethod> info) {
|
||||||
return peripheralMethods.forEachSelfMethod(target, (name, method, info) -> {
|
methods.add(new SaturatedMethod(target, name, method));
|
||||||
methods.add(new SaturatedMethod(target, name, method));
|
|
||||||
|
|
||||||
// If we have a peripheral type, use it. Always pick the smallest one, so it's consistent (assuming mods
|
// If we have a peripheral type, use it. Always pick the smallest one, so it's consistent (assuming mods
|
||||||
// don't change).
|
// don't change).
|
||||||
var type = info == null ? null : info.genericType();
|
var type = info == null ? null : info.genericType();
|
||||||
if (type != null && type.getPrimaryType() != null) {
|
if (type != null && type.getPrimaryType() != null) {
|
||||||
var primaryType = type.getPrimaryType();
|
var primaryType = type.getPrimaryType();
|
||||||
if (this.name == null || this.name.compareTo(primaryType) > 0) this.name = primaryType;
|
if (this.name == null || this.name.compareTo(primaryType) > 0) this.name = primaryType;
|
||||||
}
|
}
|
||||||
if (type != null) additionalTypes.addAll(type.getAdditionalTypes());
|
if (type != null) additionalTypes.addAll(type.getAdditionalTypes());
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,69 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2020 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.shared.peripheral.generic;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.lua.GenericSource;
|
||||||
|
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
|
import dan200.computercraft.core.methods.MethodSupplier;
|
||||||
|
import dan200.computercraft.core.methods.PeripheralMethod;
|
||||||
|
import dan200.computercraft.shared.computer.core.ServerContext;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A peripheral provider which finds methods from various {@linkplain GenericSource generic sources}.
|
||||||
|
* <p>
|
||||||
|
* Methods are found using the original block entity itself and a registered list of {@link ComponentLookup}s.
|
||||||
|
*
|
||||||
|
* @param <C> A platform-specific type, used for the invalidation callback.
|
||||||
|
*/
|
||||||
|
public final class GenericPeripheralProvider<C extends Runnable> {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(GenericPeripheralProvider.class);
|
||||||
|
|
||||||
|
private final List<ComponentLookup<? super C>> lookups = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a component lookup function.
|
||||||
|
*
|
||||||
|
* @param lookup The component lookup function.
|
||||||
|
*/
|
||||||
|
public synchronized void registerLookup(ComponentLookup<? super C> lookup) {
|
||||||
|
Objects.requireNonNull(lookup);
|
||||||
|
if (!lookups.contains(lookup)) lookups.add(lookup);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void forEachMethod(MethodSupplier<PeripheralMethod> methods, Level level, BlockPos pos, Direction side, BlockEntity blockEntity, C invalidate, MethodSupplier.TargetedConsumer<PeripheralMethod> consumer) {
|
||||||
|
methods.forEachMethod(blockEntity, consumer);
|
||||||
|
|
||||||
|
for (var lookup : lookups) {
|
||||||
|
var contents = lookup.find(level, pos, blockEntity.getBlockState(), blockEntity, side, invalidate);
|
||||||
|
if (contents != null) methods.forEachMethod(contents, consumer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public IPeripheral getPeripheral(Level level, BlockPos pos, Direction side, @Nullable BlockEntity blockEntity, C invalidate) {
|
||||||
|
if (blockEntity == null) return null;
|
||||||
|
|
||||||
|
var server = level.getServer();
|
||||||
|
if (server == null) {
|
||||||
|
LOG.warn("Fetching peripherals on a non-server level {}.", level, new IllegalStateException("Fetching peripherals on a non-server level."));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var builder = new GenericPeripheralBuilder();
|
||||||
|
forEachMethod(ServerContext.get(server).peripheralMethods(), level, pos, side, blockEntity, invalidate, builder::addMethod);
|
||||||
|
return builder.toPeripheral(blockEntity, side);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.impl;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
|
import dan200.computercraft.shared.peripheral.generic.ComponentLookup;
|
||||||
|
import dan200.computercraft.shared.peripheral.generic.GenericPeripheralProvider;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The registry for peripheral providers.
|
||||||
|
* <p>
|
||||||
|
* This lives in the {@code impl} package despite it not being part of the public API, in order to mirror Forge's class.
|
||||||
|
*/
|
||||||
|
public final class Peripherals {
|
||||||
|
private static final GenericPeripheralProvider<Runnable> genericProvider = new GenericPeripheralProvider<>();
|
||||||
|
|
||||||
|
private Peripherals() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addGenericLookup(ComponentLookup<? super Runnable> lookup) {
|
||||||
|
genericProvider.registerLookup(lookup);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @Nullable IPeripheral getGenericPeripheral(Level level, BlockPos pos, Direction side, @Nullable BlockEntity blockEntity, Runnable invalidate) {
|
||||||
|
return genericProvider.getPeripheral(level, pos, side, blockEntity, invalidate);
|
||||||
|
}
|
||||||
|
}
|
@ -8,6 +8,7 @@ import dan200.computercraft.api.ComputerCraftAPI;
|
|||||||
import dan200.computercraft.api.detail.FabricDetailRegistries;
|
import dan200.computercraft.api.detail.FabricDetailRegistries;
|
||||||
import dan200.computercraft.api.node.wired.WiredElementLookup;
|
import dan200.computercraft.api.node.wired.WiredElementLookup;
|
||||||
import dan200.computercraft.api.peripheral.PeripheralLookup;
|
import dan200.computercraft.api.peripheral.PeripheralLookup;
|
||||||
|
import dan200.computercraft.impl.Peripherals;
|
||||||
import dan200.computercraft.shared.command.CommandComputerCraft;
|
import dan200.computercraft.shared.command.CommandComputerCraft;
|
||||||
import dan200.computercraft.shared.config.Config;
|
import dan200.computercraft.shared.config.Config;
|
||||||
import dan200.computercraft.shared.config.ConfigSpec;
|
import dan200.computercraft.shared.config.ConfigSpec;
|
||||||
@ -100,6 +101,8 @@ public class ComputerCraft {
|
|||||||
FabricDetailRegistries.FLUID_VARIANT.addProvider(FluidDetails::fill);
|
FabricDetailRegistries.FLUID_VARIANT.addProvider(FluidDetails::fill);
|
||||||
|
|
||||||
ComputerCraftAPI.registerGenericSource(new InventoryMethods());
|
ComputerCraftAPI.registerGenericSource(new InventoryMethods());
|
||||||
|
|
||||||
|
Peripherals.addGenericLookup((world, pos, state, blockEntity, side, invalidate) -> InventoryMethods.extractContainer(world, pos, state, blockEntity, side));
|
||||||
}
|
}
|
||||||
|
|
||||||
private record ReloadListener(String name, PreparableReloadListener listener)
|
private record ReloadListener(String name, PreparableReloadListener listener)
|
||||||
|
@ -1,52 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2020 The CC: Tweaked Developers
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
|
||||||
|
|
||||||
package dan200.computercraft.shared.peripheral.generic;
|
|
||||||
|
|
||||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
|
||||||
import dan200.computercraft.shared.peripheral.generic.methods.InventoryMethods;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.core.Direction;
|
|
||||||
import net.minecraft.world.level.Level;
|
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class GenericPeripheralProvider {
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(GenericPeripheralProvider.class);
|
|
||||||
|
|
||||||
interface Lookup<T> {
|
|
||||||
@Nullable
|
|
||||||
T find(Level world, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, Direction context);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final List<Lookup<?>> lookups = List.of(
|
|
||||||
InventoryMethods::extractContainer
|
|
||||||
);
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public static IPeripheral getPeripheral(Level level, BlockPos pos, Direction side, @Nullable BlockEntity blockEntity) {
|
|
||||||
if (blockEntity == null) return null;
|
|
||||||
|
|
||||||
var server = level.getServer();
|
|
||||||
if (server == null) {
|
|
||||||
LOG.warn("Fetching peripherals on a non-server level {}.", level, new IllegalStateException("Fetching peripherals on a non-server level."));
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var builder = new GenericPeripheralBuilder(server);
|
|
||||||
builder.addMethods(blockEntity);
|
|
||||||
|
|
||||||
for (var lookup : lookups) {
|
|
||||||
var contents = lookup.find(level, pos, blockEntity.getBlockState(), blockEntity, side);
|
|
||||||
if (contents != null) builder.addMethods(contents);
|
|
||||||
}
|
|
||||||
|
|
||||||
return builder.toPeripheral(blockEntity, side);
|
|
||||||
}
|
|
||||||
}
|
|
@ -14,12 +14,12 @@ import dan200.computercraft.api.network.wired.WiredElement;
|
|||||||
import dan200.computercraft.api.node.wired.WiredElementLookup;
|
import dan200.computercraft.api.node.wired.WiredElementLookup;
|
||||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
import dan200.computercraft.api.peripheral.PeripheralLookup;
|
import dan200.computercraft.api.peripheral.PeripheralLookup;
|
||||||
|
import dan200.computercraft.impl.Peripherals;
|
||||||
import dan200.computercraft.mixin.ArgumentTypeInfosAccessor;
|
import dan200.computercraft.mixin.ArgumentTypeInfosAccessor;
|
||||||
import dan200.computercraft.shared.config.ConfigFile;
|
import dan200.computercraft.shared.config.ConfigFile;
|
||||||
import dan200.computercraft.shared.network.NetworkMessage;
|
import dan200.computercraft.shared.network.NetworkMessage;
|
||||||
import dan200.computercraft.shared.network.client.ClientNetworkContext;
|
import dan200.computercraft.shared.network.client.ClientNetworkContext;
|
||||||
import dan200.computercraft.shared.network.container.ContainerData;
|
import dan200.computercraft.shared.network.container.ContainerData;
|
||||||
import dan200.computercraft.shared.peripheral.generic.GenericPeripheralProvider;
|
|
||||||
import dan200.computercraft.shared.util.InventoryUtil;
|
import dan200.computercraft.shared.util.InventoryUtil;
|
||||||
import net.fabricmc.fabric.api.event.player.AttackEntityCallback;
|
import net.fabricmc.fabric.api.event.player.AttackEntityCallback;
|
||||||
import net.fabricmc.fabric.api.event.player.UseBlockCallback;
|
import net.fabricmc.fabric.api.event.player.UseBlockCallback;
|
||||||
@ -202,7 +202,7 @@ public class PlatformHelperImpl implements PlatformHelper {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ComponentAccess<IPeripheral> createPeripheralAccess(Consumer<Direction> invalidate) {
|
public ComponentAccess<IPeripheral> createPeripheralAccess(Consumer<Direction> invalidate) {
|
||||||
return new PeripheralAccessImpl();
|
return new PeripheralAccessImpl(invalidate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -476,8 +476,11 @@ public class PlatformHelperImpl implements PlatformHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static final class PeripheralAccessImpl extends ComponentAccessImpl<IPeripheral> {
|
private static final class PeripheralAccessImpl extends ComponentAccessImpl<IPeripheral> {
|
||||||
private PeripheralAccessImpl() {
|
private final Runnable[] invalidators = new Runnable[6];
|
||||||
|
|
||||||
|
private PeripheralAccessImpl(Consumer<Direction> invalidate) {
|
||||||
super(PeripheralLookup.get());
|
super(PeripheralLookup.get());
|
||||||
|
for (var dir : Direction.values()) invalidators[dir.ordinal()] = () -> invalidate.accept(dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@ -487,7 +490,8 @@ public class PlatformHelperImpl implements PlatformHelper {
|
|||||||
if (result != null) return result;
|
if (result != null) return result;
|
||||||
|
|
||||||
var cache = caches[direction.ordinal()];
|
var cache = caches[direction.ordinal()];
|
||||||
return GenericPeripheralProvider.getPeripheral(level, cache.getPos(), direction.getOpposite(), cache.getBlockEntity());
|
var invalidate = invalidators[direction.ordinal()];
|
||||||
|
return Peripherals.getGenericPeripheral(level, cache.getPos(), direction.getOpposite(), cache.getBlockEntity(), invalidate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@ import dan200.computercraft.api.network.wired.WiredElement;
|
|||||||
import dan200.computercraft.api.peripheral.IPeripheralProvider;
|
import dan200.computercraft.api.peripheral.IPeripheralProvider;
|
||||||
import dan200.computercraft.impl.detail.DetailRegistryImpl;
|
import dan200.computercraft.impl.detail.DetailRegistryImpl;
|
||||||
import dan200.computercraft.shared.details.FluidData;
|
import dan200.computercraft.shared.details.FluidData;
|
||||||
import dan200.computercraft.shared.peripheral.generic.GenericPeripheralProvider;
|
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
import net.minecraft.world.level.BlockGetter;
|
import net.minecraft.world.level.BlockGetter;
|
||||||
@ -45,7 +44,7 @@ public final class ComputerCraftAPIImpl extends AbstractComputerCraftAPI impleme
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerGenericCapability(Capability<?> capability) {
|
public void registerGenericCapability(Capability<?> capability) {
|
||||||
GenericPeripheralProvider.addCapability(capability);
|
Peripherals.registerGenericCapability(capability);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -6,12 +6,16 @@ package dan200.computercraft.impl;
|
|||||||
|
|
||||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
import dan200.computercraft.api.peripheral.IPeripheralProvider;
|
import dan200.computercraft.api.peripheral.IPeripheralProvider;
|
||||||
|
import dan200.computercraft.shared.peripheral.generic.ComponentLookup;
|
||||||
import dan200.computercraft.shared.peripheral.generic.GenericPeripheralProvider;
|
import dan200.computercraft.shared.peripheral.generic.GenericPeripheralProvider;
|
||||||
|
import dan200.computercraft.shared.platform.InvalidateCallback;
|
||||||
import dan200.computercraft.shared.util.CapabilityUtil;
|
import dan200.computercraft.shared.util.CapabilityUtil;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
import net.minecraftforge.common.util.NonNullConsumer;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraftforge.common.capabilities.Capability;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@ -22,10 +26,16 @@ import java.util.Objects;
|
|||||||
|
|
||||||
import static dan200.computercraft.shared.Capabilities.CAPABILITY_PERIPHERAL;
|
import static dan200.computercraft.shared.Capabilities.CAPABILITY_PERIPHERAL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The registry for peripheral providers.
|
||||||
|
* <p>
|
||||||
|
* This lives in the {@code impl} package despite it not being part of the public API, in order to mirror Forge's class.
|
||||||
|
*/
|
||||||
public final class Peripherals {
|
public final class Peripherals {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(Peripherals.class);
|
private static final Logger LOG = LoggerFactory.getLogger(Peripherals.class);
|
||||||
|
|
||||||
private static final Collection<IPeripheralProvider> providers = new LinkedHashSet<>();
|
private static final Collection<IPeripheralProvider> providers = new LinkedHashSet<>();
|
||||||
|
private static final GenericPeripheralProvider<InvalidateCallback> genericProvider = new GenericPeripheralProvider<>();
|
||||||
|
|
||||||
private Peripherals() {
|
private Peripherals() {
|
||||||
}
|
}
|
||||||
@ -35,13 +45,39 @@ public final class Peripherals {
|
|||||||
providers.add(provider);
|
providers.add(provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void registerGenericLookup(ComponentLookup<InvalidateCallback> lookup) {
|
||||||
|
genericProvider.registerLookup(lookup);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link ComponentLookup} for {@linkplain Capability capabilities}.
|
||||||
|
* <p>
|
||||||
|
* This is a record to ensure that adding the same capability multiple times only results in one lookup being
|
||||||
|
* present in the resulting list.
|
||||||
|
*
|
||||||
|
* @param capability The capability to lookup
|
||||||
|
* @param <T> The type of the capability we look up.
|
||||||
|
*/
|
||||||
|
private record CapabilityLookup<T>(Capability<T> capability) implements ComponentLookup<InvalidateCallback> {
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public T find(Level level, BlockPos pos, BlockState state, BlockEntity blockEntity, Direction side, InvalidateCallback invalidate) {
|
||||||
|
return CapabilityUtil.unwrap(CapabilityUtil.getCapability(blockEntity, this.capability(), side), invalidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void registerGenericCapability(Capability<?> capability) {
|
||||||
|
Objects.requireNonNull(capability, "Capability cannot be null");
|
||||||
|
registerGenericLookup(new CapabilityLookup<>(capability));
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public static IPeripheral getPeripheral(Level world, BlockPos pos, Direction side, NonNullConsumer<Object> invalidate) {
|
public static IPeripheral getPeripheral(Level world, BlockPos pos, Direction side, InvalidateCallback invalidate) {
|
||||||
return world.isInWorldBounds(pos) && !world.isClientSide ? getPeripheralAt(world, pos, side, invalidate) : null;
|
return world.isInWorldBounds(pos) && !world.isClientSide ? getPeripheralAt(world, pos, side, invalidate) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private static IPeripheral getPeripheralAt(Level world, BlockPos pos, Direction side, NonNullConsumer<? super Object> invalidate) {
|
private static IPeripheral getPeripheralAt(Level world, BlockPos pos, Direction side, InvalidateCallback invalidate) {
|
||||||
var block = world.getBlockEntity(pos);
|
var block = world.getBlockEntity(pos);
|
||||||
if (block != null) {
|
if (block != null) {
|
||||||
var peripheral = block.getCapability(CAPABILITY_PERIPHERAL, side);
|
var peripheral = block.getCapability(CAPABILITY_PERIPHERAL, side);
|
||||||
@ -58,7 +94,6 @@ public final class Peripherals {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return GenericPeripheralProvider.getPeripheral(world, pos, side, invalidate);
|
return genericProvider.getPeripheral(world, pos, side, block, invalidate);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,54 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2020 The CC: Tweaked Developers
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
|
||||||
|
|
||||||
package dan200.computercraft.shared.peripheral.generic;
|
|
||||||
|
|
||||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
|
||||||
import dan200.computercraft.shared.util.CapabilityUtil;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.core.Direction;
|
|
||||||
import net.minecraft.world.level.Level;
|
|
||||||
import net.minecraftforge.common.capabilities.Capability;
|
|
||||||
import net.minecraftforge.common.util.NonNullConsumer;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
public class GenericPeripheralProvider {
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(GenericPeripheralProvider.class);
|
|
||||||
|
|
||||||
private static final ArrayList<Capability<?>> capabilities = new ArrayList<>();
|
|
||||||
|
|
||||||
public static synchronized void addCapability(Capability<?> capability) {
|
|
||||||
Objects.requireNonNull(capability, "Capability cannot be null");
|
|
||||||
if (!capabilities.contains(capability)) capabilities.add(capability);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public static IPeripheral getPeripheral(Level level, BlockPos pos, Direction side, NonNullConsumer<Object> invalidate) {
|
|
||||||
var blockEntity = level.getBlockEntity(pos);
|
|
||||||
if (blockEntity == null) return null;
|
|
||||||
|
|
||||||
var server = level.getServer();
|
|
||||||
if (server == null) {
|
|
||||||
LOG.warn("Fetching peripherals on a non-server level {}.", level, new IllegalStateException("Fetching peripherals on a non-server level."));
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var builder = new GenericPeripheralBuilder(server);
|
|
||||||
builder.addMethods(blockEntity);
|
|
||||||
|
|
||||||
for (var capability : capabilities) {
|
|
||||||
var wrapper = CapabilityUtil.getCapability(blockEntity, capability, side);
|
|
||||||
wrapper.ifPresent(contents -> {
|
|
||||||
if (builder.addMethods(contents)) CapabilityUtil.addListener(wrapper, invalidate);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return builder.toPeripheral(blockEntity, side);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,33 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.shared.platform;
|
||||||
|
|
||||||
|
import dan200.computercraft.shared.peripheral.generic.ComponentLookup;
|
||||||
|
import net.minecraftforge.common.util.LazyOptional;
|
||||||
|
import net.minecraftforge.common.util.NonNullConsumer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A function which may be called when a capability (or some other object) has been invalidated.
|
||||||
|
* <p>
|
||||||
|
* This extends {@link NonNullConsumer} for use with {@link LazyOptional#addListener(NonNullConsumer)}, and
|
||||||
|
* {@link Runnable} for use with {@link ComponentLookup}.
|
||||||
|
*/
|
||||||
|
public interface InvalidateCallback extends Runnable, NonNullConsumer<Object> {
|
||||||
|
@Override
|
||||||
|
default void accept(Object o) {
|
||||||
|
run();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cast this callback to a {@link NonNullConsumer} of an arbitrary type.
|
||||||
|
*
|
||||||
|
* @param <T> The type of the consumer, normally a {@link LazyOptional}.
|
||||||
|
* @return {@code this}, but with a compatible type.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
default <T> NonNullConsumer<T> castConsumer() {
|
||||||
|
return (NonNullConsumer<T>) this;
|
||||||
|
}
|
||||||
|
}
|
@ -63,7 +63,6 @@ import net.minecraftforge.common.crafting.CraftingHelper;
|
|||||||
import net.minecraftforge.common.crafting.conditions.ICondition;
|
import net.minecraftforge.common.crafting.conditions.ICondition;
|
||||||
import net.minecraftforge.common.crafting.conditions.ModLoadedCondition;
|
import net.minecraftforge.common.crafting.conditions.ModLoadedCondition;
|
||||||
import net.minecraftforge.common.extensions.IForgeMenuType;
|
import net.minecraftforge.common.extensions.IForgeMenuType;
|
||||||
import net.minecraftforge.common.util.NonNullConsumer;
|
|
||||||
import net.minecraftforge.event.ForgeEventFactory;
|
import net.minecraftforge.event.ForgeEventFactory;
|
||||||
import net.minecraftforge.eventbus.api.Event;
|
import net.minecraftforge.eventbus.api.Event;
|
||||||
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
||||||
@ -429,20 +428,19 @@ public class PlatformHelperImpl implements PlatformHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private abstract static class ComponentAccessImpl<T> implements ComponentAccess<T> {
|
private abstract static class ComponentAccessImpl<T> implements ComponentAccess<T> {
|
||||||
private final NonNullConsumer<Object>[] invalidators;
|
private final InvalidateCallback[] invalidators;
|
||||||
private @Nullable Level level;
|
private @Nullable Level level;
|
||||||
private @Nullable BlockPos pos;
|
private @Nullable BlockPos pos;
|
||||||
|
|
||||||
ComponentAccessImpl(Consumer<Direction> invalidate) {
|
ComponentAccessImpl(Consumer<Direction> invalidate) {
|
||||||
// Generate a cache of invalidation functions so we can guarantee we only ever have one registered per
|
// Generate a cache of invalidation functions so we can guarantee we only ever have one registered per
|
||||||
// capability - there's no way to remove these callbacks!
|
// capability - there's no way to remove these callbacks!
|
||||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
var invalidators = this.invalidators = new InvalidateCallback[6];
|
||||||
var invalidators = this.invalidators = new NonNullConsumer[6];
|
for (var dir : Direction.values()) invalidators[dir.ordinal()] = () -> invalidate.accept(dir);
|
||||||
for (var dir : Direction.values()) invalidators[dir.ordinal()] = x -> invalidate.accept(dir);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
protected abstract T get(ServerLevel world, BlockPos pos, Direction side, NonNullConsumer<Object> invalidate);
|
protected abstract T get(ServerLevel world, BlockPos pos, Direction side, InvalidateCallback invalidate);
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
@ -463,7 +461,7 @@ public class PlatformHelperImpl implements PlatformHelper {
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
protected IPeripheral get(ServerLevel world, BlockPos pos, Direction side, NonNullConsumer<Object> invalidate) {
|
protected IPeripheral get(ServerLevel world, BlockPos pos, Direction side, InvalidateCallback invalidate) {
|
||||||
return Peripherals.getPeripheral(world, pos, side, invalidate);
|
return Peripherals.getPeripheral(world, pos, side, invalidate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -478,7 +476,7 @@ public class PlatformHelperImpl implements PlatformHelper {
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
protected T get(ServerLevel world, BlockPos pos, Direction side, NonNullConsumer<Object> invalidate) {
|
protected T get(ServerLevel world, BlockPos pos, Direction side, InvalidateCallback invalidate) {
|
||||||
if (!world.isLoaded(pos)) return null;
|
if (!world.isLoaded(pos)) return null;
|
||||||
|
|
||||||
var blockEntity = world.getBlockEntity(pos);
|
var blockEntity = world.getBlockEntity(pos);
|
||||||
|
@ -4,11 +4,11 @@
|
|||||||
|
|
||||||
package dan200.computercraft.shared.util;
|
package dan200.computercraft.shared.util;
|
||||||
|
|
||||||
|
import dan200.computercraft.shared.platform.InvalidateCallback;
|
||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
import net.minecraftforge.common.capabilities.Capability;
|
import net.minecraftforge.common.capabilities.Capability;
|
||||||
import net.minecraftforge.common.capabilities.ICapabilityProvider;
|
import net.minecraftforge.common.capabilities.ICapabilityProvider;
|
||||||
import net.minecraftforge.common.util.LazyOptional;
|
import net.minecraftforge.common.util.LazyOptional;
|
||||||
import net.minecraftforge.common.util.NonNullConsumer;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
@ -32,18 +32,11 @@ public final class CapabilityUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> void addListener(LazyOptional<T> p, NonNullConsumer<? super LazyOptional<T>> invalidate) {
|
|
||||||
// We can make this safe with invalidate::accept, but then we're allocating it's just kind of absurd.
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
var safeInvalidate = (NonNullConsumer<LazyOptional<T>>) invalidate;
|
|
||||||
p.addListener(safeInvalidate);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public static <T> T unwrap(LazyOptional<T> p, NonNullConsumer<? super LazyOptional<T>> invalidate) {
|
public static <T> T unwrap(LazyOptional<T> p, InvalidateCallback invalidate) {
|
||||||
if (!p.isPresent()) return null;
|
if (!p.isPresent()) return null;
|
||||||
|
|
||||||
addListener(p, invalidate);
|
p.addListener(invalidate.castConsumer());
|
||||||
return p.orElseThrow(NullPointerException::new);
|
return p.orElseThrow(NullPointerException::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user