diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml
index 6cc5fe765..03959f78e 100644
--- a/config/checkstyle/checkstyle.xml
+++ b/config/checkstyle/checkstyle.xml
@@ -114,11 +114,11 @@
-
+
-
+
diff --git a/src/main/java/dan200/computercraft/ComputerCraftAPIImpl.java b/src/main/java/dan200/computercraft/ComputerCraftAPIImpl.java
index f8bf288d8..1610ea5fb 100644
--- a/src/main/java/dan200/computercraft/ComputerCraftAPIImpl.java
+++ b/src/main/java/dan200/computercraft/ComputerCraftAPIImpl.java
@@ -24,7 +24,6 @@
import dan200.computercraft.shared.*;
import dan200.computercraft.shared.peripheral.modem.wireless.WirelessNetwork;
import dan200.computercraft.shared.util.IDAssigner;
-import dan200.computercraft.shared.wired.CapabilityWiredElement;
import dan200.computercraft.shared.wired.WiredNode;
import net.minecraft.resources.IReloadableResourceManager;
import net.minecraft.tileentity.TileEntity;
@@ -41,6 +40,8 @@
import java.lang.ref.WeakReference;
import java.util.Map;
+import static dan200.computercraft.shared.Capabilities.CAPABILITY_WIRED_ELEMENT;
+
public final class ComputerCraftAPIImpl implements IComputerCraftAPI
{
public static final ComputerCraftAPIImpl INSTANCE = new ComputerCraftAPIImpl();
@@ -147,6 +148,6 @@ public IWiredNode createWiredNodeForElement( @Nonnull IWiredElement element )
public LazyOptional getWiredElementAt( @Nonnull IBlockReader world, @Nonnull BlockPos pos, @Nonnull Direction side )
{
TileEntity tile = world.getTileEntity( pos );
- return tile == null ? LazyOptional.empty() : tile.getCapability( CapabilityWiredElement.CAPABILITY, side );
+ return tile == null ? LazyOptional.empty() : tile.getCapability( CAPABILITY_WIRED_ELEMENT, side );
}
}
diff --git a/src/main/java/dan200/computercraft/api/peripheral/IPeripheral.java b/src/main/java/dan200/computercraft/api/peripheral/IPeripheral.java
index a361848c3..04cfa6440 100644
--- a/src/main/java/dan200/computercraft/api/peripheral/IPeripheral.java
+++ b/src/main/java/dan200/computercraft/api/peripheral/IPeripheral.java
@@ -6,13 +6,17 @@
package dan200.computercraft.api.peripheral;
import dan200.computercraft.api.lua.LuaFunction;
+import net.minecraftforge.common.capabilities.Capability;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
- * The interface that defines a peripheral. See {@link IPeripheralProvider} for how to associate blocks with
- * peripherals.
+ * The interface that defines a peripheral.
+ *
+ * In order to expose a peripheral for your block or tile entity, you may either attach a {@link Capability}, or
+ * register a {@link IPeripheralProvider}. It is not recommended to implement {@link IPeripheral} directly on
+ * the tile.
*
* Peripherals should provide a series of methods to the user, either using {@link LuaFunction} or by implementing
* {@link IDynamicPeripheral}.
diff --git a/src/main/java/dan200/computercraft/api/peripheral/IPeripheralProvider.java b/src/main/java/dan200/computercraft/api/peripheral/IPeripheralProvider.java
index 3df8aa708..2be4f613e 100644
--- a/src/main/java/dan200/computercraft/api/peripheral/IPeripheralProvider.java
+++ b/src/main/java/dan200/computercraft/api/peripheral/IPeripheralProvider.java
@@ -9,14 +9,15 @@
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
+import net.minecraftforge.common.util.LazyOptional;
import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
/**
* This interface is used to create peripheral implementations for blocks.
*
- * If you have a {@link TileEntity} which acts as a peripheral, you may alternatively implement {@link IPeripheralTile}.
+ * If you have a {@link TileEntity} which acts as a peripheral, you may alternatively expose the {@link IPeripheral}
+ * capability.
*
* @see dan200.computercraft.api.ComputerCraftAPI#registerPeripheralProvider(IPeripheralProvider)
*/
@@ -29,9 +30,9 @@ public interface IPeripheralProvider
* @param world The world the block is in.
* @param pos The position the block is at.
* @param side The side to get the peripheral from.
- * @return A peripheral, or {@code null} if there is not a peripheral here you'd like to handle.
+ * @return A peripheral, or {@link LazyOptional#empty()} if there is not a peripheral here you'd like to handle.
* @see dan200.computercraft.api.ComputerCraftAPI#registerPeripheralProvider(IPeripheralProvider)
*/
- @Nullable
- IPeripheral getPeripheral( @Nonnull World world, @Nonnull BlockPos pos, @Nonnull Direction side );
+ @Nonnull
+ LazyOptional getPeripheral( @Nonnull World world, @Nonnull BlockPos pos, @Nonnull Direction side );
}
diff --git a/src/main/java/dan200/computercraft/api/peripheral/IPeripheralTile.java b/src/main/java/dan200/computercraft/api/peripheral/IPeripheralTile.java
deleted file mode 100644
index ce64116c2..000000000
--- a/src/main/java/dan200/computercraft/api/peripheral/IPeripheralTile.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * This file is part of the public ComputerCraft API - http://www.computercraft.info
- * Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only.
- * For help using the API, and posting your mods, visit the forums at computercraft.info.
- */
-package dan200.computercraft.api.peripheral;
-
-import net.minecraft.util.Direction;
-import net.minecraft.util.math.BlockPos;
-import net.minecraft.world.World;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-/**
- * A {@link net.minecraft.tileentity.TileEntity} which may act as a peripheral.
- *
- * If you need more complex capabilities (such as handling TEs not belonging to your mod), you should use
- * {@link IPeripheralProvider}.
- */
-public interface IPeripheralTile
-{
- /**
- * Get the peripheral on the given {@code side}.
- *
- * @param side The side to get the peripheral from.
- * @return A peripheral, or {@code null} if there is not a peripheral here.
- * @see IPeripheralProvider#getPeripheral(World, BlockPos, Direction)
- */
- @Nullable
- IPeripheral getPeripheral( @Nonnull Direction side );
-}
diff --git a/src/main/java/dan200/computercraft/shared/Capabilities.java b/src/main/java/dan200/computercraft/shared/Capabilities.java
new file mode 100644
index 000000000..ba820e183
--- /dev/null
+++ b/src/main/java/dan200/computercraft/shared/Capabilities.java
@@ -0,0 +1,25 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+
+package dan200.computercraft.shared;
+
+import dan200.computercraft.api.network.wired.IWiredElement;
+import dan200.computercraft.api.peripheral.IPeripheral;
+import net.minecraftforge.common.capabilities.Capability;
+import net.minecraftforge.common.capabilities.CapabilityInject;
+
+public final class Capabilities
+{
+ @CapabilityInject( IPeripheral.class )
+ public static Capability CAPABILITY_PERIPHERAL = null;
+
+ @CapabilityInject( IWiredElement.class )
+ public static Capability CAPABILITY_WIRED_ELEMENT = null;
+
+ private Capabilities()
+ {
+ }
+}
diff --git a/src/main/java/dan200/computercraft/shared/Peripherals.java b/src/main/java/dan200/computercraft/shared/Peripherals.java
index 021884d71..aae2b9d6d 100644
--- a/src/main/java/dan200/computercraft/shared/Peripherals.java
+++ b/src/main/java/dan200/computercraft/shared/Peripherals.java
@@ -8,15 +8,22 @@
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.peripheral.IPeripheralProvider;
+import dan200.computercraft.shared.util.CapabilityUtil;
+import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
+import net.minecraftforge.common.util.LazyOptional;
+import net.minecraftforge.common.util.NonNullConsumer;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Objects;
+import static dan200.computercraft.shared.Capabilities.CAPABILITY_PERIPHERAL;
+
public final class Peripherals
{
private static final Collection providers = new LinkedHashSet<>();
@@ -29,20 +36,29 @@ public static synchronized void register( @Nonnull IPeripheralProvider provider
providers.add( provider );
}
- public static IPeripheral getPeripheral( World world, BlockPos pos, Direction side )
+ @Nullable
+ public static IPeripheral getPeripheral( World world, BlockPos pos, Direction side, NonNullConsumer> invalidate )
{
- return World.isValid( pos ) && !world.isRemote ? getPeripheralAt( world, pos, side ) : null;
+ return World.isValid( pos ) && !world.isRemote ? getPeripheralAt( world, pos, side, invalidate ) : null;
}
- private static IPeripheral getPeripheralAt( World world, BlockPos pos, Direction side )
+ @Nullable
+ private static IPeripheral getPeripheralAt( World world, BlockPos pos, Direction side, NonNullConsumer> invalidate )
{
+ TileEntity block = world.getTileEntity( pos );
+ if( block != null )
+ {
+ LazyOptional peripheral = block.getCapability( CAPABILITY_PERIPHERAL, side );
+ if( peripheral.isPresent() ) return CapabilityUtil.unwrap( peripheral, invalidate );
+ }
+
// Try the handlers in order:
for( IPeripheralProvider peripheralProvider : providers )
{
try
{
- IPeripheral peripheral = peripheralProvider.getPeripheral( world, pos, side );
- if( peripheral != null ) return peripheral;
+ LazyOptional peripheral = peripheralProvider.getPeripheral( world, pos, side );
+ if( peripheral.isPresent() ) return CapabilityUtil.unwrap( peripheral, invalidate );
}
catch( Exception e )
{
diff --git a/src/main/java/dan200/computercraft/shared/common/BlockGeneric.java b/src/main/java/dan200/computercraft/shared/common/BlockGeneric.java
index 81a4f45f5..b7cad192e 100644
--- a/src/main/java/dan200/computercraft/shared/common/BlockGeneric.java
+++ b/src/main/java/dan200/computercraft/shared/common/BlockGeneric.java
@@ -50,7 +50,7 @@ public final void onReplaced( @Nonnull BlockState block, @Nonnull World world, @
@Nonnull
@Override
@Deprecated
- public final ActionResultType onBlockActivated( BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult hit )
+ public final ActionResultType onBlockActivated( @Nonnull BlockState state, World world, @Nonnull BlockPos pos, @Nonnull PlayerEntity player, @Nonnull Hand hand, @Nonnull BlockRayTraceResult hit )
{
TileEntity tile = world.getTileEntity( pos );
return tile instanceof TileGeneric ? ((TileGeneric) tile).onActivate( player, hand, hit ) : ActionResultType.PASS;
@@ -58,7 +58,7 @@ public final ActionResultType onBlockActivated( BlockState state, World world, B
@Override
@Deprecated
- public final void neighborChanged( BlockState state, World world, BlockPos pos, Block neighbourBlock, BlockPos neighbourPos, boolean isMoving )
+ public final void neighborChanged( @Nonnull BlockState state, World world, @Nonnull BlockPos pos, @Nonnull Block neighbourBlock, @Nonnull BlockPos neighbourPos, boolean isMoving )
{
TileEntity tile = world.getTileEntity( pos );
if( tile instanceof TileGeneric ) ((TileGeneric) tile).onNeighbourChange( neighbourPos );
@@ -73,7 +73,7 @@ public final void onNeighborChange( BlockState state, IWorldReader world, BlockP
@Override
@Deprecated
- public void tick( BlockState state, ServerWorld world, BlockPos pos, Random rand )
+ public void tick( @Nonnull BlockState state, ServerWorld world, @Nonnull BlockPos pos, @Nonnull Random rand )
{
TileEntity te = world.getTileEntity( pos );
if( te instanceof TileGeneric ) ((TileGeneric) te).blockTick();
diff --git a/src/main/java/dan200/computercraft/shared/computer/blocks/ComputerPeripheral.java b/src/main/java/dan200/computercraft/shared/computer/blocks/ComputerPeripheral.java
index 71b394256..15c3395b9 100644
--- a/src/main/java/dan200/computercraft/shared/computer/blocks/ComputerPeripheral.java
+++ b/src/main/java/dan200/computercraft/shared/computer/blocks/ComputerPeripheral.java
@@ -67,7 +67,7 @@ public final String getLabel()
@Override
public boolean equals( IPeripheral other )
{
- return other != null && other.getClass() == getClass();
+ return other instanceof ComputerPeripheral && computer == ((ComputerPeripheral) other).computer;
}
@Nonnull
diff --git a/src/main/java/dan200/computercraft/shared/computer/blocks/ComputerProxy.java b/src/main/java/dan200/computercraft/shared/computer/blocks/ComputerProxy.java
index 810d72c57..205161aeb 100644
--- a/src/main/java/dan200/computercraft/shared/computer/blocks/ComputerProxy.java
+++ b/src/main/java/dan200/computercraft/shared/computer/blocks/ComputerProxy.java
@@ -8,12 +8,24 @@
import dan200.computercraft.shared.computer.core.IComputer;
import dan200.computercraft.shared.computer.core.ServerComputer;
+import java.util.function.Supplier;
+
/**
* A proxy object for computer objects, delegating to {@link IComputer} or {@link TileComputer} where appropriate.
*/
-public abstract class ComputerProxy
+public final class ComputerProxy
{
- protected abstract TileComputerBase getTile();
+ private final Supplier get;
+
+ public ComputerProxy( Supplier get )
+ {
+ this.get = get;
+ }
+
+ protected TileComputerBase getTile()
+ {
+ return get.get();
+ }
public void turnOn()
{
diff --git a/src/main/java/dan200/computercraft/shared/computer/blocks/TileComputer.java b/src/main/java/dan200/computercraft/shared/computer/blocks/TileComputer.java
index 1ac0c94ad..34a768af3 100644
--- a/src/main/java/dan200/computercraft/shared/computer/blocks/TileComputer.java
+++ b/src/main/java/dan200/computercraft/shared/computer/blocks/TileComputer.java
@@ -6,11 +6,13 @@
package dan200.computercraft.shared.computer.blocks;
import dan200.computercraft.ComputerCraft;
+import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.core.computer.ComputerSide;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.core.ComputerState;
import dan200.computercraft.shared.computer.core.ServerComputer;
import dan200.computercraft.shared.computer.inventory.ContainerComputer;
+import dan200.computercraft.shared.util.CapabilityUtil;
import dan200.computercraft.shared.util.NamedTileEntityType;
import net.minecraft.block.BlockState;
import net.minecraft.entity.player.PlayerEntity;
@@ -19,10 +21,14 @@
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.util.Direction;
import net.minecraft.util.ResourceLocation;
+import net.minecraftforge.common.capabilities.Capability;
+import net.minecraftforge.common.util.LazyOptional;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+import static dan200.computercraft.shared.Capabilities.CAPABILITY_PERIPHERAL;
+
public class TileComputer extends TileComputerBase
{
public static final NamedTileEntityType FACTORY_NORMAL = NamedTileEntityType.create(
@@ -35,7 +41,8 @@ public class TileComputer extends TileComputerBase
f -> new TileComputer( ComputerFamily.ADVANCED, f )
);
- private ComputerProxy m_proxy;
+ private ComputerProxy proxy;
+ private LazyOptional peripheral;
public TileComputer( ComputerFamily family, TileEntityType extends TileComputer> type )
{
@@ -55,23 +62,6 @@ protected ServerComputer createComputer( int instanceID, int id )
return computer;
}
- @Override
- public ComputerProxy createProxy()
- {
- if( m_proxy == null )
- {
- m_proxy = new ComputerProxy()
- {
- @Override
- protected TileComputerBase getTile()
- {
- return TileComputer.this;
- }
- };
- }
- return m_proxy;
- }
-
public boolean isUsableByPlayer( PlayerEntity player )
{
return isUsable( player, false );
@@ -109,4 +99,30 @@ public Container createMenu( int id, @Nonnull PlayerInventory inventory, @Nonnul
{
return new ContainerComputer( id, this );
}
+
+ @Nonnull
+ @Override
+ public LazyOptional getCapability( @Nonnull Capability cap, @Nullable Direction side )
+ {
+ if( cap == CAPABILITY_PERIPHERAL )
+ {
+ if( peripheral == null )
+ {
+ peripheral = LazyOptional.of( () -> {
+ if( proxy == null ) proxy = new ComputerProxy( () -> this );
+ return new ComputerPeripheral( "computer", proxy );
+ } );
+ }
+ return peripheral.cast();
+ }
+
+ return super.getCapability( cap, side );
+ }
+
+ @Override
+ protected void invalidateCaps()
+ {
+ super.invalidateCaps();
+ peripheral = CapabilityUtil.invalidate( peripheral );
+ }
}
diff --git a/src/main/java/dan200/computercraft/shared/computer/blocks/TileComputerBase.java b/src/main/java/dan200/computercraft/shared/computer/blocks/TileComputerBase.java
index 652700e9a..9bf2df439 100644
--- a/src/main/java/dan200/computercraft/shared/computer/blocks/TileComputerBase.java
+++ b/src/main/java/dan200/computercraft/shared/computer/blocks/TileComputerBase.java
@@ -7,7 +7,6 @@
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.peripheral.IPeripheral;
-import dan200.computercraft.api.peripheral.IPeripheralTile;
import dan200.computercraft.core.computer.ComputerSide;
import dan200.computercraft.shared.BundledRedstone;
import dan200.computercraft.shared.Peripherals;
@@ -45,7 +44,7 @@
import javax.annotation.Nullable;
import java.util.Objects;
-public abstract class TileComputerBase extends TileGeneric implements IComputerTile, ITickableTileEntity, IPeripheralTile, INameable, INamedContainerProvider
+public abstract class TileComputerBase extends TileGeneric implements IComputerTile, ITickableTileEntity, INameable, INamedContainerProvider
{
private static final String NBT_ID = "ComputerId";
private static final String NBT_LABEL = "Label";
@@ -226,7 +225,8 @@ private void updateSideInput( ServerComputer computer, Direction dir, BlockPos o
computer.setBundledRedstoneInput( localDir, BundledRedstone.getOutput( getWorld(), offset, offsetSide ) );
if( !isPeripheralBlockedOnSide( localDir ) )
{
- computer.setPeripheral( localDir, Peripherals.getPeripheral( getWorld(), offset, offsetSide ) );
+ IPeripheral peripheral = Peripherals.getPeripheral( getWorld(), offset, offsetSide, o -> updateInput( dir ) );
+ computer.setPeripheral( localDir, peripheral );
}
}
@@ -272,7 +272,6 @@ private void updateInput( BlockPos neighbour )
ServerComputer computer = getServerComputer();
if( computer == null ) return;
- BlockPos pos = computer.getPosition();
for( Direction dir : DirectionUtil.FACINGS )
{
BlockPos offset = pos.offset( dir );
@@ -287,6 +286,16 @@ private void updateInput( BlockPos neighbour )
updateInput();
}
+ private void updateInput( Direction dir )
+ {
+ if( getWorld() == null || getWorld().isRemote ) return;
+
+ ServerComputer computer = getServerComputer();
+ if( computer == null ) return;
+
+ updateSideInput( computer, dir, pos.offset( dir ) );
+ }
+
public void updateOutput()
{
// Update redstone
@@ -299,8 +308,6 @@ public void updateOutput()
protected abstract ServerComputer createComputer( int instanceID, int id );
- public abstract ComputerProxy createProxy();
-
@Override
public final int getComputerID()
{
@@ -404,13 +411,6 @@ protected void transferStateFrom( TileComputerBase copy )
copy.m_instanceID = -1;
}
- @Nullable
- @Override
- public IPeripheral getPeripheral( @Nonnull Direction side )
- {
- return new ComputerPeripheral( "computer", createProxy() );
- }
-
@Nonnull
@Override
public ITextComponent getName()
diff --git a/src/main/java/dan200/computercraft/shared/peripheral/commandblock/CommandBlockPeripheral.java b/src/main/java/dan200/computercraft/shared/peripheral/commandblock/CommandBlockPeripheral.java
index 383f65f62..1ba941959 100644
--- a/src/main/java/dan200/computercraft/shared/peripheral/commandblock/CommandBlockPeripheral.java
+++ b/src/main/java/dan200/computercraft/shared/peripheral/commandblock/CommandBlockPeripheral.java
@@ -5,15 +5,33 @@
*/
package dan200.computercraft.shared.peripheral.commandblock;
+import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.lua.LuaFunction;
import dan200.computercraft.api.peripheral.IPeripheral;
+import dan200.computercraft.shared.util.CapabilityUtil;
import net.minecraft.tileentity.CommandBlockTileEntity;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.Direction;
+import net.minecraft.util.ResourceLocation;
+import net.minecraftforge.common.capabilities.Capability;
+import net.minecraftforge.common.capabilities.ICapabilityProvider;
+import net.minecraftforge.common.util.LazyOptional;
+import net.minecraftforge.event.AttachCapabilitiesEvent;
+import net.minecraftforge.eventbus.api.SubscribeEvent;
+import net.minecraftforge.fml.common.Mod;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
-public class CommandBlockPeripheral implements IPeripheral
+import static dan200.computercraft.shared.Capabilities.CAPABILITY_PERIPHERAL;
+
+@Mod.EventBusSubscriber
+public class CommandBlockPeripheral implements IPeripheral, ICapabilityProvider
{
+ private static final ResourceLocation CAP_ID = new ResourceLocation( ComputerCraft.MOD_ID, "command_block" );
+
private final CommandBlockTileEntity commandBlock;
+ private LazyOptional self;
public CommandBlockPeripheral( CommandBlockTileEntity commandBlock )
{
@@ -53,4 +71,33 @@ public boolean equals( IPeripheral other )
{
return other != null && other.getClass() == getClass();
}
+
+ @Nonnull
+ @Override
+ public LazyOptional getCapability( @Nonnull Capability cap, @Nullable Direction side )
+ {
+ if( cap == CAPABILITY_PERIPHERAL )
+ {
+ if( self == null ) self = LazyOptional.of( () -> this );
+ return self.cast();
+ }
+ return LazyOptional.empty();
+ }
+
+ private void invalidate()
+ {
+ self = CapabilityUtil.invalidate( self );
+ }
+
+ @SubscribeEvent
+ public static void onCapability( AttachCapabilitiesEvent event )
+ {
+ TileEntity tile = event.getObject();
+ if( tile instanceof CommandBlockTileEntity )
+ {
+ CommandBlockPeripheral peripheral = new CommandBlockPeripheral( (CommandBlockTileEntity) tile );
+ event.addCapability( CAP_ID, peripheral );
+ event.addListener( peripheral::invalidate );
+ }
+ }
}
diff --git a/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/TileDiskDrive.java b/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/TileDiskDrive.java
index a6337d1dc..fbbfc4bef 100644
--- a/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/TileDiskDrive.java
+++ b/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/TileDiskDrive.java
@@ -11,13 +11,9 @@
import dan200.computercraft.api.media.IMedia;
import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.peripheral.IPeripheral;
-import dan200.computercraft.api.peripheral.IPeripheralTile;
import dan200.computercraft.shared.MediaProviders;
import dan200.computercraft.shared.common.TileGeneric;
-import dan200.computercraft.shared.util.DefaultInventory;
-import dan200.computercraft.shared.util.InventoryUtil;
-import dan200.computercraft.shared.util.NamedTileEntityType;
-import dan200.computercraft.shared.util.RecordUtil;
+import dan200.computercraft.shared.util.*;
import net.minecraft.block.BlockState;
import net.minecraft.entity.item.ItemEntity;
import net.minecraft.entity.player.PlayerEntity;
@@ -45,9 +41,10 @@
import java.util.Map;
import java.util.Set;
+import static dan200.computercraft.shared.Capabilities.CAPABILITY_PERIPHERAL;
import static net.minecraftforge.items.CapabilityItemHandler.ITEM_HANDLER_CAPABILITY;
-public final class TileDiskDrive extends TileGeneric implements DefaultInventory, ITickableTileEntity, IPeripheralTile, INameable, INamedContainerProvider
+public final class TileDiskDrive extends TileGeneric implements DefaultInventory, ITickableTileEntity, INameable, INamedContainerProvider
{
private static final String NBT_NAME = "CustomName";
private static final String NBT_ITEM = "Item";
@@ -69,6 +66,7 @@ private static class MountInfo
@Nonnull
private ItemStack m_diskStack = ItemStack.EMPTY;
private LazyOptional itemHandlerCap;
+ private LazyOptional peripheralCap;
private IMount m_diskMount = null;
private boolean m_recordQueued = false;
@@ -92,11 +90,8 @@ public void destroy()
protected void invalidateCaps()
{
super.invalidateCaps();
- if( itemHandlerCap != null )
- {
- itemHandlerCap.invalidate();
- itemHandlerCap = null;
- }
+ itemHandlerCap = CapabilityUtil.invalidate( itemHandlerCap );
+ peripheralCap = CapabilityUtil.invalidate( peripheralCap );
}
@Nonnull
@@ -313,12 +308,6 @@ public void clear()
setInventorySlotContents( 0, ItemStack.EMPTY );
}
- @Override
- public IPeripheral getPeripheral( @Nonnull Direction side )
- {
- return new DiskDrivePeripheral( this );
- }
-
@Nonnull
ItemStack getDiskStack()
{
@@ -535,6 +524,13 @@ public LazyOptional getCapability( @Nonnull Capability cap, @Nullable
if( itemHandlerCap == null ) itemHandlerCap = LazyOptional.of( () -> new InvWrapper( this ) );
return itemHandlerCap.cast();
}
+
+ if( cap == CAPABILITY_PERIPHERAL )
+ {
+ if( peripheralCap == null ) peripheralCap = LazyOptional.of( () -> new DiskDrivePeripheral( this ) );
+ return peripheralCap.cast();
+ }
+
return super.getCapability( cap, side );
}
diff --git a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/CableModemVariant.java b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/CableModemVariant.java
index be3630a05..22d4b5ead 100644
--- a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/CableModemVariant.java
+++ b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/CableModemVariant.java
@@ -9,6 +9,7 @@
import net.minecraft.util.IStringSerializable;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
public enum CableModemVariant implements IStringSerializable
{
@@ -69,6 +70,7 @@ public String getName()
return name;
}
+ @Nullable
public Direction getFacing()
{
return facing;
diff --git a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileCable.java b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileCable.java
index bfe602ce0..8652b33f5 100644
--- a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileCable.java
+++ b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileCable.java
@@ -11,14 +11,13 @@
import dan200.computercraft.api.network.wired.IWiredElement;
import dan200.computercraft.api.network.wired.IWiredNode;
import dan200.computercraft.api.peripheral.IPeripheral;
-import dan200.computercraft.api.peripheral.IPeripheralTile;
import dan200.computercraft.shared.command.CommandCopy;
import dan200.computercraft.shared.common.TileGeneric;
import dan200.computercraft.shared.peripheral.modem.ModemState;
+import dan200.computercraft.shared.util.CapabilityUtil;
import dan200.computercraft.shared.util.DirectionUtil;
import dan200.computercraft.shared.util.NamedTileEntityType;
import dan200.computercraft.shared.util.TickScheduler;
-import dan200.computercraft.shared.wired.CapabilityWiredElement;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.entity.player.PlayerEntity;
@@ -42,7 +41,10 @@
import java.util.Collections;
import java.util.Map;
-public class TileCable extends TileGeneric implements IPeripheralTile
+import static dan200.computercraft.shared.Capabilities.CAPABILITY_PERIPHERAL;
+import static dan200.computercraft.shared.Capabilities.CAPABILITY_WIRED_ELEMENT;
+
+public class TileCable extends TileGeneric
{
public static final NamedTileEntityType FACTORY = NamedTileEntityType.create(
new ResourceLocation( ComputerCraft.MOD_ID, "cable" ),
@@ -82,7 +84,7 @@ protected void detachPeripheral( String name )
}
private boolean m_peripheralAccessAllowed;
- private WiredModemLocalPeripheral m_peripheral = new WiredModemLocalPeripheral();
+ private final WiredModemLocalPeripheral m_peripheral = new WiredModemLocalPeripheral( this::refreshPeripheral );
private boolean m_destroyed = false;
@@ -113,6 +115,7 @@ public Vec3d getPosition()
return new Vec3d( pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5 );
}
};
+ private LazyOptional modemCap;
private final NonNullConsumer> connectedNodeChanged = x -> connectionsChanged();
@@ -159,11 +162,8 @@ public void remove()
protected void invalidateCaps()
{
super.invalidateCaps();
- if( elementCap != null )
- {
- elementCap.invalidate();
- elementCap = null;
- }
+ elementCap = CapabilityUtil.invalidate( elementCap );
+ modemCap = CapabilityUtil.invalidate( modemCap );
}
@Override
@@ -181,20 +181,26 @@ public void updateContainingBlockInfo()
if( !world.isRemote ) world.getPendingBlockTicks().scheduleTick( pos, getBlockState().getBlock(), 0 );
}
- private void updateDirection()
+ private void refreshDirection()
{
- if( !hasModemDirection )
- {
- hasModemDirection = true;
- modemDirection = getDirection();
- }
+ if( hasModemDirection ) return;
+
+ hasModemDirection = true;
+ modemDirection = getBlockState().get( BlockCable.MODEM ).getFacing();
}
+ @Nullable
+ private Direction getMaybeDirection()
+ {
+ refreshDirection();
+ return modemDirection;
+ }
+
+ @Nonnull
private Direction getDirection()
{
- BlockState state = getBlockState();
- Direction facing = state.get( BlockCable.MODEM ).getFacing();
- return facing != null ? facing : Direction.NORTH;
+ refreshDirection();
+ return modemDirection == null ? Direction.NORTH : modemDirection;
}
@Override
@@ -232,10 +238,15 @@ public void onNeighbourTileEntityChange( @Nonnull BlockPos neighbour )
if( !world.isRemote && m_peripheralAccessAllowed )
{
Direction facing = getDirection();
- if( getPos().offset( facing ).equals( neighbour ) )
- {
- if( m_peripheral.attach( world, getPos(), facing ) ) updateConnectedPeripherals();
- }
+ if( getPos().offset( facing ).equals( neighbour ) ) refreshPeripheral();
+ }
+ }
+
+ private void refreshPeripheral()
+ {
+ if( world != null && !isRemoved() && m_peripheral.attach( world, getPos(), getDirection() ) )
+ {
+ updateConnectedPeripherals();
}
}
@@ -303,7 +314,14 @@ public void blockTick()
{
if( getWorld().isRemote ) return;
- updateDirection();
+ Direction oldDirection = modemDirection;
+ refreshDirection();
+ if( modemDirection != oldDirection )
+ {
+ // We invalidate both the modem and element if the modem's direction is different.
+ modemCap = CapabilityUtil.invalidate( modemCap );
+ elementCap = CapabilityUtil.invalidate( elementCap );
+ }
if( m_modem.getModemState().pollChanged() ) updateBlockState();
@@ -353,11 +371,7 @@ else if( m_node.getNetwork() == node.getNetwork() )
void modemChanged()
{
// Tell anyone who cares that the connection state has changed
- if( elementCap != null )
- {
- elementCap.invalidate();
- elementCap = null;
- }
+ elementCap = CapabilityUtil.invalidate( elementCap );
if( getWorld().isRemote ) return;
@@ -415,22 +429,24 @@ public boolean canRenderBreaking()
@Nonnull
@Override
- public LazyOptional getCapability( @Nonnull Capability capability, @Nullable Direction facing )
+ public LazyOptional getCapability( @Nonnull Capability capability, @Nullable Direction side )
{
- if( capability == CapabilityWiredElement.CAPABILITY )
+ if( capability == CAPABILITY_WIRED_ELEMENT )
{
- if( m_destroyed || !BlockCable.canConnectIn( getBlockState(), facing ) ) return LazyOptional.empty();
+ if( m_destroyed || !BlockCable.canConnectIn( getBlockState(), side ) ) return LazyOptional.empty();
if( elementCap == null ) elementCap = LazyOptional.of( () -> m_cable );
return elementCap.cast();
}
- return super.getCapability( capability, facing );
- }
+ if( capability == CAPABILITY_PERIPHERAL )
+ {
+ refreshDirection();
+ if( side != null && getMaybeDirection() != side ) return LazyOptional.empty();
+ if( modemCap == null ) modemCap = LazyOptional.of( () -> m_modem );
+ return modemCap.cast();
+ }
- @Override
- public IPeripheral getPeripheral( @Nonnull Direction side )
- {
- return !m_destroyed && hasModem() && side == getDirection() ? m_modem : null;
+ return super.getCapability( capability, side );
}
boolean hasCable()
diff --git a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileWiredModemFull.java b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileWiredModemFull.java
index 84aa21a32..97d38f76e 100644
--- a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileWiredModemFull.java
+++ b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileWiredModemFull.java
@@ -11,14 +11,10 @@
import dan200.computercraft.api.network.wired.IWiredElement;
import dan200.computercraft.api.network.wired.IWiredNode;
import dan200.computercraft.api.peripheral.IPeripheral;
-import dan200.computercraft.api.peripheral.IPeripheralTile;
import dan200.computercraft.shared.command.CommandCopy;
import dan200.computercraft.shared.common.TileGeneric;
import dan200.computercraft.shared.peripheral.modem.ModemState;
-import dan200.computercraft.shared.util.DirectionUtil;
-import dan200.computercraft.shared.util.NamedTileEntityType;
-import dan200.computercraft.shared.util.TickScheduler;
-import dan200.computercraft.shared.wired.CapabilityWiredElement;
+import dan200.computercraft.shared.util.*;
import net.minecraft.block.BlockState;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.nbt.CompoundNBT;
@@ -40,10 +36,12 @@
import javax.annotation.Nullable;
import java.util.*;
+import static dan200.computercraft.shared.Capabilities.CAPABILITY_PERIPHERAL;
+import static dan200.computercraft.shared.Capabilities.CAPABILITY_WIRED_ELEMENT;
import static dan200.computercraft.shared.peripheral.modem.wired.BlockWiredModemFull.MODEM_ON;
import static dan200.computercraft.shared.peripheral.modem.wired.BlockWiredModemFull.PERIPHERAL_ON;
-public class TileWiredModemFull extends TileGeneric implements IPeripheralTile
+public class TileWiredModemFull extends TileGeneric
{
public static final NamedTileEntityType FACTORY = NamedTileEntityType.create(
new ResourceLocation( ComputerCraft.MOD_ID, "wired_modem_full" ),
@@ -66,7 +64,7 @@ protected void attachPeripheral( String name, IPeripheral peripheral )
{
for( int i = 0; i < 6; i++ )
{
- WiredModemPeripheral modem = m_entity.m_modems[i];
+ WiredModemPeripheral modem = m_entity.modems[i];
if( modem != null ) modem.attachPeripheral( name, peripheral );
}
}
@@ -76,7 +74,7 @@ protected void detachPeripheral( String name )
{
for( int i = 0; i < 6; i++ )
{
- WiredModemPeripheral modem = m_entity.m_modems[i];
+ WiredModemPeripheral modem = m_entity.modems[i];
if( modem != null ) modem.detachPeripheral( name );
}
}
@@ -97,10 +95,11 @@ public Vec3d getPosition()
}
}
- private WiredModemPeripheral[] m_modems = new WiredModemPeripheral[6];
+ private final WiredModemPeripheral[] modems = new WiredModemPeripheral[6];
+ private final SidedCaps modemCaps = SidedCaps.ofNonNull( this::getPeripheral );
private boolean m_peripheralAccessAllowed = false;
- private WiredModemLocalPeripheral[] m_peripherals = new WiredModemLocalPeripheral[6];
+ private final WiredModemLocalPeripheral[] m_peripherals = new WiredModemLocalPeripheral[6];
private boolean m_destroyed = false;
private boolean m_connectionsFormed = false;
@@ -115,7 +114,11 @@ public Vec3d getPosition()
public TileWiredModemFull()
{
super( FACTORY );
- for( int i = 0; i < m_peripherals.length; i++ ) m_peripherals[i] = new WiredModemLocalPeripheral();
+ for( int i = 0; i < m_peripherals.length; i++ )
+ {
+ Direction facing = Direction.byIndex( i );
+ m_peripherals[i] = new WiredModemLocalPeripheral( () -> refreshPeripheral( facing ) );
+ }
}
private void doRemove()
@@ -149,11 +152,8 @@ public void onChunkUnloaded()
protected void invalidateCaps()
{
super.invalidateCaps();
- if( elementCap != null )
- {
- elementCap.invalidate();
- elementCap = null;
- }
+ elementCap = CapabilityUtil.invalidate( elementCap );
+ modemCaps.invalidate();
}
@Override
@@ -176,15 +176,20 @@ public void onNeighbourTileEntityChange( @Nonnull BlockPos neighbour )
{
for( Direction facing : DirectionUtil.FACINGS )
{
- if( getPos().offset( facing ).equals( neighbour ) )
- {
- WiredModemLocalPeripheral peripheral = m_peripherals[facing.ordinal()];
- if( peripheral.attach( world, getPos(), facing ) ) updateConnectedPeripherals();
- }
+ if( getPos().offset( facing ).equals( neighbour ) ) refreshPeripheral( facing );
}
}
}
+ private void refreshPeripheral( @Nonnull Direction facing )
+ {
+ WiredModemLocalPeripheral peripheral = m_peripherals[facing.ordinal()];
+ if( world != null && !isRemoved() && peripheral.attach( world, getPos(), facing ) )
+ {
+ updateConnectedPeripherals();
+ }
+ }
+
@Nonnull
@Override
public ActionResultType onActivate( PlayerEntity player, Hand hand, BlockRayTraceResult hit )
@@ -223,7 +228,7 @@ private static void sendPeripheralChanges( PlayerEntity player, String kind, Col
}
@Override
- public void read( CompoundNBT nbt )
+ public void read( @Nonnull CompoundNBT nbt )
{
super.read( nbt );
m_peripheralAccessAllowed = nbt.getBoolean( NBT_PERIPHERAL_ENABLED );
@@ -362,14 +367,17 @@ private void updateConnectedPeripherals()
@Nonnull
@Override
- public LazyOptional getCapability( @Nonnull Capability capability, @Nullable Direction facing )
+ public LazyOptional getCapability( @Nonnull Capability capability, @Nullable Direction side )
{
- if( capability == CapabilityWiredElement.CAPABILITY )
+ if( capability == CAPABILITY_WIRED_ELEMENT )
{
if( elementCap == null ) elementCap = LazyOptional.of( () -> m_element );
return elementCap.cast();
}
- return super.getCapability( capability, facing );
+
+ if( capability == CAPABILITY_PERIPHERAL ) return modemCaps.get( side ).cast();
+
+ return super.getCapability( capability, side );
}
public IWiredElement getElement()
@@ -377,35 +385,28 @@ public IWiredElement getElement()
return m_element;
}
- // IPeripheralTile
-
- @Override
- public IPeripheral getPeripheral( @Nonnull Direction side )
+ private WiredModemPeripheral getPeripheral( @Nonnull Direction side )
{
- if( m_destroyed ) return null;
+ WiredModemPeripheral peripheral = modems[side.ordinal()];
+ if( peripheral != null ) return peripheral;
- WiredModemPeripheral peripheral = m_modems[side.ordinal()];
- if( peripheral == null )
+ WiredModemLocalPeripheral localPeripheral = m_peripherals[side.ordinal()];
+ return modems[side.ordinal()] = new WiredModemPeripheral( m_modemState, m_element )
{
- WiredModemLocalPeripheral localPeripheral = m_peripherals[side.ordinal()];
- peripheral = m_modems[side.ordinal()] = new WiredModemPeripheral( m_modemState, m_element )
+ @Nonnull
+ @Override
+ protected WiredModemLocalPeripheral getLocalPeripheral()
{
- @Nonnull
- @Override
- protected WiredModemLocalPeripheral getLocalPeripheral()
- {
- return localPeripheral;
- }
+ return localPeripheral;
+ }
- @Nonnull
- @Override
- public Vec3d getPosition()
- {
- BlockPos pos = getPos().offset( side );
- return new Vec3d( pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5 );
- }
- };
- }
- return peripheral;
+ @Nonnull
+ @Override
+ public Vec3d getPosition()
+ {
+ BlockPos pos = getPos().offset( side );
+ return new Vec3d( pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5 );
+ }
+ };
}
}
diff --git a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/WiredModemLocalPeripheral.java b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/WiredModemLocalPeripheral.java
index 9c776cf76..9314c4d74 100644
--- a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/WiredModemLocalPeripheral.java
+++ b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/WiredModemLocalPeripheral.java
@@ -15,6 +15,8 @@
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.common.util.Constants;
+import net.minecraftforge.common.util.LazyOptional;
+import net.minecraftforge.common.util.NonNullConsumer;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -36,6 +38,12 @@ public final class WiredModemLocalPeripheral
private String type;
private IPeripheral peripheral;
+ private final NonNullConsumer> invalidate;
+
+ public WiredModemLocalPeripheral( @Nonnull Runnable invalidate )
+ {
+ this.invalidate = x -> invalidate.run();
+ }
/**
* Attach a new peripheral from the world.
@@ -130,14 +138,15 @@ public void read( @Nonnull CompoundNBT tag, @Nonnull String suffix )
? tag.getString( NBT_PERIPHERAL_TYPE + suffix ) : null;
}
- private static IPeripheral getPeripheralFrom( World world, BlockPos pos, Direction direction )
+ @Nullable
+ private IPeripheral getPeripheralFrom( World world, BlockPos pos, Direction direction )
{
BlockPos offset = pos.offset( direction );
Block block = world.getBlockState( offset ).getBlock();
if( block == ComputerCraft.Blocks.wiredModemFull || block == ComputerCraft.Blocks.cable ) return null;
- IPeripheral peripheral = Peripherals.getPeripheral( world, offset, direction.getOpposite() );
+ IPeripheral peripheral = Peripherals.getPeripheral( world, offset, direction.getOpposite(), invalidate );
return peripheral instanceof WiredModemPeripheral ? null : peripheral;
}
}
diff --git a/src/main/java/dan200/computercraft/shared/peripheral/modem/wireless/TileWirelessModem.java b/src/main/java/dan200/computercraft/shared/peripheral/modem/wireless/TileWirelessModem.java
index 557aee0d4..33373e566 100644
--- a/src/main/java/dan200/computercraft/shared/peripheral/modem/wireless/TileWirelessModem.java
+++ b/src/main/java/dan200/computercraft/shared/peripheral/modem/wireless/TileWirelessModem.java
@@ -7,10 +7,10 @@
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.peripheral.IPeripheral;
-import dan200.computercraft.api.peripheral.IPeripheralTile;
import dan200.computercraft.shared.common.TileGeneric;
import dan200.computercraft.shared.peripheral.modem.ModemPeripheral;
import dan200.computercraft.shared.peripheral.modem.ModemState;
+import dan200.computercraft.shared.util.CapabilityUtil;
import dan200.computercraft.shared.util.NamedTileEntityType;
import dan200.computercraft.shared.util.TickScheduler;
import net.minecraft.block.BlockState;
@@ -20,11 +20,15 @@
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
+import net.minecraftforge.common.capabilities.Capability;
+import net.minecraftforge.common.util.LazyOptional;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
-public class TileWirelessModem extends TileGeneric implements IPeripheralTile
+import static dan200.computercraft.shared.Capabilities.CAPABILITY_PERIPHERAL;
+
+public class TileWirelessModem extends TileGeneric
{
public static final NamedTileEntityType FACTORY_NORMAL = NamedTileEntityType.create(
new ResourceLocation( ComputerCraft.MOD_ID, "wireless_modem_normal" ),
@@ -74,6 +78,7 @@ public boolean equals( IPeripheral other )
private Direction modemDirection = Direction.DOWN;
private final ModemPeripheral modem;
private boolean destroyed = false;
+ private LazyOptional modemCap;
public TileWirelessModem( TileEntityType extends TileWirelessModem> type, boolean advanced )
{
@@ -99,20 +104,6 @@ public void destroy()
}
}
- @Override
- public void markDirty()
- {
- super.markDirty();
- if( world != null )
- {
- updateDirection();
- }
- else
- {
- hasModemDirection = false;
- }
- }
-
@Override
public void updateContainingBlockInfo()
{
@@ -124,12 +115,17 @@ public void updateContainingBlockInfo()
@Override
public void blockTick()
{
- updateDirection();
+ Direction currentDirection = modemDirection;
+ refreshDirection();
+ // Invalidate the capability if the direction has changed. I'm not 100% happy with this implementation
+ // - ideally we'd do it within refreshDirection or updateContainingBlockInfo, but this seems the _safest_
+ // place.
+ if( currentDirection != modemDirection ) modemCap = CapabilityUtil.invalidate( modemCap );
if( modem.getModemState().pollChanged() ) updateBlockState();
}
- private void updateDirection()
+ private void refreshDirection()
{
if( hasModemDirection ) return;
@@ -147,12 +143,18 @@ private void updateBlockState()
}
}
-
- @Nullable
+ @Nonnull
@Override
- public IPeripheral getPeripheral( @Nonnull Direction side )
+ public LazyOptional getCapability( @Nonnull Capability cap, @Nullable Direction side )
{
- updateDirection();
- return side == modemDirection ? modem : null;
+ if( cap == CAPABILITY_PERIPHERAL )
+ {
+ refreshDirection();
+ if( side != null && modemDirection != side ) return LazyOptional.empty();
+ if( modemCap == null ) modemCap = LazyOptional.of( () -> modem );
+ return modemCap.cast();
+ }
+
+ return super.getCapability( cap, side );
}
}
diff --git a/src/main/java/dan200/computercraft/shared/peripheral/monitor/TileMonitor.java b/src/main/java/dan200/computercraft/shared/peripheral/monitor/TileMonitor.java
index ff185fffc..e338d80fa 100644
--- a/src/main/java/dan200/computercraft/shared/peripheral/monitor/TileMonitor.java
+++ b/src/main/java/dan200/computercraft/shared/peripheral/monitor/TileMonitor.java
@@ -8,10 +8,10 @@
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.peripheral.IPeripheral;
-import dan200.computercraft.api.peripheral.IPeripheralTile;
import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.shared.common.ServerTerminal;
import dan200.computercraft.shared.common.TileGeneric;
+import dan200.computercraft.shared.util.CapabilityUtil;
import dan200.computercraft.shared.util.NamedTileEntityType;
import dan200.computercraft.shared.util.TickScheduler;
import net.minecraft.entity.player.PlayerEntity;
@@ -26,12 +26,17 @@
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.world.World;
+import net.minecraftforge.common.capabilities.Capability;
+import net.minecraftforge.common.util.LazyOptional;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import java.util.HashSet;
import java.util.Set;
-public class TileMonitor extends TileGeneric implements IPeripheralTile
+import static dan200.computercraft.shared.Capabilities.CAPABILITY_PERIPHERAL;
+
+public class TileMonitor extends TileGeneric
{
public static final NamedTileEntityType FACTORY_NORMAL = NamedTileEntityType.create(
new ResourceLocation( ComputerCraft.MOD_ID, "monitor_normal" ),
@@ -59,7 +64,8 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile
private ServerMonitor m_serverMonitor;
private ClientMonitor m_clientMonitor;
- private MonitorPeripheral m_peripheral;
+ private MonitorPeripheral peripheral;
+ private LazyOptional peripheralCap;
private final Set m_computers = new HashSet<>();
private boolean m_destroyed = false;
@@ -174,14 +180,25 @@ public void blockTick()
if( m_serverMonitor.pollTerminalChanged() ) updateBlock();
}
- // IPeripheralTile implementation
-
@Override
- public IPeripheral getPeripheral( @Nonnull Direction side )
+ protected void invalidateCaps()
{
- createServerMonitor(); // Ensure the monitor is created before doing anything else.
- if( m_peripheral == null ) m_peripheral = new MonitorPeripheral( this );
- return m_peripheral;
+ super.invalidateCaps();
+ peripheralCap = CapabilityUtil.invalidate( peripheralCap );
+ }
+
+ @Nonnull
+ @Override
+ public LazyOptional getCapability( @Nonnull Capability cap, @Nullable Direction side )
+ {
+ if( cap == CAPABILITY_PERIPHERAL )
+ {
+ createServerMonitor(); // Ensure the monitor is created before doing anything else.
+ if( peripheral == null ) peripheral = new MonitorPeripheral( this );
+ if( peripheralCap == null ) peripheralCap = LazyOptional.of( () -> peripheral );
+ return peripheralCap.cast();
+ }
+ return super.getCapability( cap, side );
}
public ServerMonitor getCachedServerMonitor()
@@ -409,7 +426,7 @@ private void resize( int width, int height )
for( int y = 0; y < height; y++ )
{
TileMonitor monitor = getNeighbour( x, y );
- if( monitor != null && monitor.m_peripheral != null )
+ if( monitor != null && monitor.peripheral != null )
{
needsTerminal = true;
break terminalCheck;
diff --git a/src/main/java/dan200/computercraft/shared/peripheral/printer/TilePrinter.java b/src/main/java/dan200/computercraft/shared/peripheral/printer/TilePrinter.java
index 529ec7d23..65513f569 100644
--- a/src/main/java/dan200/computercraft/shared/peripheral/printer/TilePrinter.java
+++ b/src/main/java/dan200/computercraft/shared/peripheral/printer/TilePrinter.java
@@ -7,14 +7,10 @@
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.peripheral.IPeripheral;
-import dan200.computercraft.api.peripheral.IPeripheralTile;
import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.shared.common.TileGeneric;
import dan200.computercraft.shared.media.items.ItemPrintout;
-import dan200.computercraft.shared.util.ColourUtils;
-import dan200.computercraft.shared.util.DefaultSidedInventory;
-import dan200.computercraft.shared.util.NamedTileEntityType;
-import dan200.computercraft.shared.util.WorldUtil;
+import dan200.computercraft.shared.util.*;
import net.minecraft.block.BlockState;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
@@ -32,16 +28,17 @@
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.fml.network.NetworkHooks;
-import net.minecraftforge.items.IItemHandlerModifiable;
+import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.wrapper.InvWrapper;
import net.minecraftforge.items.wrapper.SidedInvWrapper;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+import static dan200.computercraft.shared.Capabilities.CAPABILITY_PERIPHERAL;
import static net.minecraftforge.items.CapabilityItemHandler.ITEM_HANDLER_CAPABILITY;
-public final class TilePrinter extends TileGeneric implements DefaultSidedInventory, IPeripheralTile, INameable, INamedContainerProvider
+public final class TilePrinter extends TileGeneric implements DefaultSidedInventory, INameable, INamedContainerProvider
{
public static final NamedTileEntityType FACTORY = NamedTileEntityType.create(
new ResourceLocation( ComputerCraft.MOD_ID, "printer" ),
@@ -61,7 +58,9 @@ public final class TilePrinter extends TileGeneric implements DefaultSidedInvent
ITextComponent customName;
private final NonNullList m_inventory = NonNullList.withSize( SLOTS, ItemStack.EMPTY );
- private LazyOptional[] itemHandlerCaps;
+ private final SidedCaps itemHandlerCaps =
+ SidedCaps.ofNullable( facing -> facing == null ? new InvWrapper( this ) : new SidedInvWrapper( this, facing ) );
+ private LazyOptional peripheralCap;
private final Terminal m_page = new Terminal( ItemPrintout.LINE_MAX_LENGTH, ItemPrintout.LINES_PER_PAGE );
private String m_pageTitle = "";
@@ -82,16 +81,8 @@ public void destroy()
protected void invalidateCaps()
{
super.invalidateCaps();
-
- if( itemHandlerCaps != null )
- {
- for( int i = 0; i < itemHandlerCaps.length; i++ )
- {
- if( itemHandlerCaps[i] == null ) continue;
- itemHandlerCaps[i].invalidate();
- itemHandlerCaps[i] = null;
- }
- }
+ itemHandlerCaps.invalidate();
+ peripheralCap = CapabilityUtil.invalidate( peripheralCap );
}
@Nonnull
@@ -262,14 +253,6 @@ public int[] getSlotsForFace( @Nonnull Direction side )
}
}
- // IPeripheralTile implementation
-
- @Override
- public IPeripheral getPeripheral( @Nonnull Direction side )
- {
- return new PrinterPeripheral( this );
- }
-
@Nullable
Terminal getCurrentPage()
{
@@ -465,26 +448,15 @@ private void updateBlockState( boolean top, boolean bottom )
getWorld().setBlockState( getPos(), state.with( BlockPrinter.TOP, top ).with( BlockPrinter.BOTTOM, bottom ) );
}
- @SuppressWarnings( { "unchecked", "rawtypes" } )
@Nonnull
@Override
public LazyOptional getCapability( @Nonnull Capability capability, @Nullable Direction facing )
{
- if( capability == ITEM_HANDLER_CAPABILITY )
+ if( capability == ITEM_HANDLER_CAPABILITY ) return itemHandlerCaps.get( facing ).cast();
+ if( capability == CAPABILITY_PERIPHERAL )
{
- LazyOptional[] handlers = itemHandlerCaps;
- if( handlers == null ) handlers = itemHandlerCaps = new LazyOptional[7];
-
- int index = facing == null ? 0 : 1 + facing.getIndex();
- LazyOptional handler = handlers[index];
- if( handler == null )
- {
- handler = handlers[index] = facing == null
- ? LazyOptional.of( () -> new InvWrapper( this ) )
- : LazyOptional.of( () -> new SidedInvWrapper( this, facing ) );
- }
-
- return handler.cast();
+ if( peripheralCap == null ) peripheralCap = LazyOptional.of( () -> new PrinterPeripheral( this ) );
+ return peripheralCap.cast();
}
return super.getCapability( capability, facing );
diff --git a/src/main/java/dan200/computercraft/shared/peripheral/speaker/TileSpeaker.java b/src/main/java/dan200/computercraft/shared/peripheral/speaker/TileSpeaker.java
index 2ca05a11e..16fb6e5c2 100644
--- a/src/main/java/dan200/computercraft/shared/peripheral/speaker/TileSpeaker.java
+++ b/src/main/java/dan200/computercraft/shared/peripheral/speaker/TileSpeaker.java
@@ -7,8 +7,8 @@
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.peripheral.IPeripheral;
-import dan200.computercraft.api.peripheral.IPeripheralTile;
import dan200.computercraft.shared.common.TileGeneric;
+import dan200.computercraft.shared.util.CapabilityUtil;
import dan200.computercraft.shared.util.NamedTileEntityType;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.util.Direction;
@@ -16,11 +16,15 @@
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
+import net.minecraftforge.common.capabilities.Capability;
+import net.minecraftforge.common.util.LazyOptional;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
-public class TileSpeaker extends TileGeneric implements ITickableTileEntity, IPeripheralTile
+import static dan200.computercraft.shared.Capabilities.CAPABILITY_PERIPHERAL;
+
+public class TileSpeaker extends TileGeneric implements ITickableTileEntity
{
public static final int MIN_TICKS_BETWEEN_SOUNDS = 1;
@@ -29,24 +33,39 @@ public class TileSpeaker extends TileGeneric implements ITickableTileEntity, IPe
TileSpeaker::new
);
- private final SpeakerPeripheral m_peripheral;
+ private final SpeakerPeripheral peripheral;
+ private LazyOptional peripheralCap;
public TileSpeaker()
{
super( FACTORY );
- m_peripheral = new Peripheral( this );
+ peripheral = new Peripheral( this );
}
@Override
public void tick()
{
- m_peripheral.update();
+ peripheral.update();
+ }
+
+ @Nonnull
+ @Override
+ public LazyOptional getCapability( @Nonnull Capability cap, @Nullable Direction side )
+ {
+ if( cap == CAPABILITY_PERIPHERAL )
+ {
+ if( peripheralCap == null ) peripheralCap = LazyOptional.of( () -> peripheral );
+ return peripheralCap.cast();
+ }
+
+ return super.getCapability( cap, side );
}
@Override
- public IPeripheral getPeripheral( @Nonnull Direction side )
+ protected void invalidateCaps()
{
- return m_peripheral;
+ super.invalidateCaps();
+ peripheralCap = CapabilityUtil.invalidate( peripheralCap );
}
private static final class Peripheral extends SpeakerPeripheral
diff --git a/src/main/java/dan200/computercraft/shared/proxy/ComputerCraftProxyCommon.java b/src/main/java/dan200/computercraft/shared/proxy/ComputerCraftProxyCommon.java
index d22234ee9..d4b962cb3 100644
--- a/src/main/java/dan200/computercraft/shared/proxy/ComputerCraftProxyCommon.java
+++ b/src/main/java/dan200/computercraft/shared/proxy/ComputerCraftProxyCommon.java
@@ -8,7 +8,8 @@
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.media.IMedia;
-import dan200.computercraft.api.peripheral.IPeripheralTile;
+import dan200.computercraft.api.network.wired.IWiredElement;
+import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.core.computer.MainThread;
import dan200.computercraft.core.tracking.Tracking;
import dan200.computercraft.shared.command.CommandComputerCraft;
@@ -23,20 +24,18 @@
import dan200.computercraft.shared.data.PlayerCreativeLootCondition;
import dan200.computercraft.shared.media.items.RecordMedia;
import dan200.computercraft.shared.network.NetworkHandler;
-import dan200.computercraft.shared.peripheral.commandblock.CommandBlockPeripheral;
import dan200.computercraft.shared.peripheral.modem.wireless.WirelessNetwork;
-import dan200.computercraft.shared.wired.CapabilityWiredElement;
+import dan200.computercraft.shared.util.NullStorage;
import net.minecraft.inventory.container.Container;
import net.minecraft.item.Item;
import net.minecraft.item.MusicDiscItem;
-import net.minecraft.tileentity.CommandBlockTileEntity;
-import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.storage.loot.ConstantRange;
import net.minecraft.world.storage.loot.LootPool;
import net.minecraft.world.storage.loot.LootTables;
import net.minecraft.world.storage.loot.TableLootEntry;
import net.minecraft.world.storage.loot.conditions.LootConditionManager;
+import net.minecraftforge.common.capabilities.CapabilityManager;
import net.minecraftforge.event.LootTableLoadEvent;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.event.entity.player.PlayerContainerEvent;
@@ -90,17 +89,6 @@ public static void registerLoot()
private static void registerProviders()
{
- // Register peripheral providers
- ComputerCraftAPI.registerPeripheralProvider( ( world, pos, side ) -> {
- TileEntity tile = world.getTileEntity( pos );
- return tile instanceof IPeripheralTile ? ((IPeripheralTile) tile).getPeripheral( side ) : null;
- } );
-
- ComputerCraftAPI.registerPeripheralProvider( ( world, pos, side ) -> {
- TileEntity tile = world.getTileEntity( pos );
- return ComputerCraft.enableCommandBlock && tile instanceof CommandBlockTileEntity ? new CommandBlockPeripheral( (CommandBlockTileEntity) tile ) : null;
- } );
-
// Register bundled power providers
ComputerCraftAPI.registerBundledRedstoneProvider( new DefaultBundledRedstoneProvider() );
@@ -112,8 +100,9 @@ private static void registerProviders()
return null;
} );
- // Register network providers
- CapabilityWiredElement.register();
+ // Register capabilities
+ CapabilityManager.INSTANCE.register( IWiredElement.class, new NullStorage<>(), () -> null );
+ CapabilityManager.INSTANCE.register( IPeripheral.class, new NullStorage<>(), () -> null );
}
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID )
diff --git a/src/main/java/dan200/computercraft/shared/turtle/blocks/TileTurtle.java b/src/main/java/dan200/computercraft/shared/turtle/blocks/TileTurtle.java
index f3a6d22e7..7954d83c2 100644
--- a/src/main/java/dan200/computercraft/shared/turtle/blocks/TileTurtle.java
+++ b/src/main/java/dan200/computercraft/shared/turtle/blocks/TileTurtle.java
@@ -47,6 +47,7 @@
import javax.annotation.Nullable;
import java.util.Collections;
+import static dan200.computercraft.shared.Capabilities.CAPABILITY_PERIPHERAL;
import static net.minecraftforge.items.CapabilityItemHandler.ITEM_HANDLER_CAPABILITY;
public class TileTurtle extends TileComputerBase implements ITurtleTile, DefaultInventory
@@ -79,6 +80,7 @@ enum MoveState
private boolean m_inventoryChanged = false;
private TurtleBrain m_brain = new TurtleBrain( this );
private MoveState m_moveState = MoveState.NOT_MOVED;
+ private LazyOptional peripheral;
public TileTurtle( TileEntityType extends TileGeneric> type, ComputerFamily family )
{
@@ -103,7 +105,6 @@ protected ServerComputer createComputer( int instanceID, int id )
return computer;
}
- @Override
public ComputerProxy createProxy()
{
return m_brain.getProxy();
@@ -154,11 +155,8 @@ protected void unload()
protected void invalidateCaps()
{
super.invalidateCaps();
- if( itemHandlerCap != null )
- {
- itemHandlerCap.invalidate();
- itemHandlerCap = null;
- }
+ itemHandlerCap = CapabilityUtil.invalidate( itemHandlerCap );
+ peripheral = CapabilityUtil.invalidate( peripheral );
}
@Nonnull
@@ -545,14 +543,10 @@ public void transferStateFrom( TileTurtle copy )
m_inventoryChanged = copy.m_inventoryChanged;
m_brain = copy.m_brain;
m_brain.setOwner( this );
- copy.m_moveState = MoveState.MOVED;
- }
- @Nullable
- @Override
- public IPeripheral getPeripheral( @Nonnull Direction side )
- {
- return hasMoved() ? null : new ComputerPeripheral( "turtle", createProxy() );
+ // Mark the other turtle as having moved, and so its peripheral is dead.
+ copy.m_moveState = MoveState.MOVED;
+ copy.peripheral = CapabilityUtil.invalidate( copy.peripheral );
}
public IItemHandlerModifiable getItemHandler()
@@ -569,6 +563,17 @@ public LazyOptional getCapability( @Nonnull Capability cap, @Nullable
if( itemHandlerCap == null ) itemHandlerCap = LazyOptional.of( () -> new InvWrapper( this ) );
return itemHandlerCap.cast();
}
+
+ if( cap == CAPABILITY_PERIPHERAL )
+ {
+ if( hasMoved() ) return LazyOptional.empty();
+ if( peripheral == null )
+ {
+ peripheral = LazyOptional.of( () -> new ComputerPeripheral( "turtle", createProxy() ) );
+ }
+ return peripheral.cast();
+ }
+
return super.getCapability( cap, side );
}
diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java
index 28eaded9b..31b363aa3 100644
--- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java
+++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java
@@ -15,7 +15,6 @@
import dan200.computercraft.core.computer.ComputerSide;
import dan200.computercraft.shared.TurtleUpgrades;
import dan200.computercraft.shared.computer.blocks.ComputerProxy;
-import dan200.computercraft.shared.computer.blocks.TileComputerBase;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.core.ServerComputer;
import dan200.computercraft.shared.turtle.blocks.TileTurtle;
@@ -72,12 +71,12 @@ public class TurtleBrain implements ITurtleAccess
private final IInventory m_inventory = (InventoryDelegate) () -> m_owner;
private final IItemHandlerModifiable m_inventoryWrapper = new InvWrapper( m_inventory );
- private Queue m_commandQueue = new ArrayDeque<>();
+ private final Queue m_commandQueue = new ArrayDeque<>();
private int m_commandsIssued = 0;
- private Map m_upgrades = new EnumMap<>( TurtleSide.class );
- private Map peripherals = new EnumMap<>( TurtleSide.class );
- private Map m_upgradeNBTData = new EnumMap<>( TurtleSide.class );
+ private final Map m_upgrades = new EnumMap<>( TurtleSide.class );
+ private final Map peripherals = new EnumMap<>( TurtleSide.class );
+ private final Map m_upgradeNBTData = new EnumMap<>( TurtleSide.class );
private int m_selectedSlot = 0;
private int m_fuelLevel = 0;
@@ -107,17 +106,7 @@ public TileTurtle getOwner()
public ComputerProxy getProxy()
{
- if( m_proxy == null )
- {
- m_proxy = new ComputerProxy()
- {
- @Override
- protected TileComputerBase getTile()
- {
- return m_owner;
- }
- };
- }
+ if( m_proxy == null ) m_proxy = new ComputerProxy( () -> m_owner );
return m_proxy;
}
diff --git a/src/main/java/dan200/computercraft/shared/util/CapabilityUtil.java b/src/main/java/dan200/computercraft/shared/util/CapabilityUtil.java
new file mode 100644
index 000000000..7643eeb2c
--- /dev/null
+++ b/src/main/java/dan200/computercraft/shared/util/CapabilityUtil.java
@@ -0,0 +1,47 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+
+package dan200.computercraft.shared.util;
+
+import net.minecraftforge.common.util.LazyOptional;
+import net.minecraftforge.common.util.NonNullConsumer;
+
+import javax.annotation.Nullable;
+
+public final class CapabilityUtil
+{
+ private CapabilityUtil()
+ {
+ }
+
+ @Nullable
+ public static LazyOptional invalidate( @Nullable LazyOptional cap )
+ {
+ if( cap != null ) cap.invalidate();
+ return null;
+ }
+
+ public static void invalidate( @Nullable LazyOptional[] caps )
+ {
+ if( caps == null ) return;
+
+ for( int i = 0; i < caps.length; i++ )
+ {
+ LazyOptional cap = caps[i];
+ if( cap != null ) cap.invalidate();
+ caps[i] = null;
+ }
+ }
+
+ @Nullable
+ public static T unwrap( LazyOptional p, NonNullConsumer> invalidate )
+ {
+ if( !p.isPresent() ) return null;
+
+ p.addListener( invalidate );
+ return p.orElseThrow( NullPointerException::new );
+ }
+}
diff --git a/src/main/java/dan200/computercraft/shared/util/NullStorage.java b/src/main/java/dan200/computercraft/shared/util/NullStorage.java
new file mode 100644
index 000000000..1c8ab93ae
--- /dev/null
+++ b/src/main/java/dan200/computercraft/shared/util/NullStorage.java
@@ -0,0 +1,25 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+
+package dan200.computercraft.shared.util;
+
+import net.minecraft.nbt.INBT;
+import net.minecraft.util.Direction;
+import net.minecraftforge.common.capabilities.Capability;
+
+public class NullStorage implements Capability.IStorage
+{
+ @Override
+ public INBT writeNBT( Capability capability, T instance, Direction side )
+ {
+ return null;
+ }
+
+ @Override
+ public void readNBT( Capability capability, T instance, Direction side, INBT base )
+ {
+ }
+}
diff --git a/src/main/java/dan200/computercraft/shared/util/SidedCaps.java b/src/main/java/dan200/computercraft/shared/util/SidedCaps.java
new file mode 100644
index 000000000..7491416a7
--- /dev/null
+++ b/src/main/java/dan200/computercraft/shared/util/SidedCaps.java
@@ -0,0 +1,68 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+
+package dan200.computercraft.shared.util;
+
+import net.minecraft.util.Direction;
+import net.minecraftforge.common.util.LazyOptional;
+
+import javax.annotation.Nullable;
+import java.util.function.Function;
+
+/**
+ * Provides a constant (but invalidate-able) capability for each side.
+ *
+ * @param The type of the produced capability.
+ */
+public final class SidedCaps
+{
+ private final Function factory;
+ private final boolean allowNull;
+ private T[] values;
+ private LazyOptional[] caps;
+
+ private SidedCaps( Function factory, boolean allowNull )
+ {
+ this.factory = factory;
+ this.allowNull = allowNull;
+ }
+
+ public static SidedCaps ofNonNull( Function factory )
+ {
+ return new SidedCaps<>( factory, false );
+ }
+
+ public static SidedCaps ofNullable( Function factory )
+ {
+ return new SidedCaps<>( factory, true );
+ }
+
+ @SuppressWarnings( { "unchecked", "rawtypes" } )
+ public LazyOptional get( @Nullable Direction direction )
+ {
+ if( direction == null && !allowNull ) return LazyOptional.empty();
+ int index = direction == null ? 6 : direction.ordinal();
+
+ LazyOptional[] caps = this.caps;
+ if( caps == null )
+ {
+ caps = this.caps = new LazyOptional[allowNull ? 7 : 6];
+ values = (T[]) new Object[caps.length];
+ }
+
+ LazyOptional cap = caps[index];
+ return cap == null ? caps[index] = LazyOptional.of( () -> {
+ T[] values = this.values;
+ T value = values[index];
+ return value == null ? values[index] = factory.apply( direction ) : value;
+ } ) : cap;
+ }
+
+ public void invalidate()
+ {
+ if( caps != null ) CapabilityUtil.invalidate( caps );
+ }
+}
diff --git a/src/main/java/dan200/computercraft/shared/wired/CapabilityWiredElement.java b/src/main/java/dan200/computercraft/shared/wired/CapabilityWiredElement.java
deleted file mode 100644
index 4681021a1..000000000
--- a/src/main/java/dan200/computercraft/shared/wired/CapabilityWiredElement.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * This file is part of ComputerCraft - http://www.computercraft.info
- * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
- * Send enquiries to dratcliffe@gmail.com
- */
-package dan200.computercraft.shared.wired;
-
-import dan200.computercraft.api.network.wired.IWiredElement;
-import dan200.computercraft.api.network.wired.IWiredNode;
-import net.minecraft.nbt.INBT;
-import net.minecraft.util.Direction;
-import net.minecraft.util.math.Vec3d;
-import net.minecraft.world.World;
-import net.minecraftforge.common.capabilities.Capability;
-import net.minecraftforge.common.capabilities.CapabilityInject;
-import net.minecraftforge.common.capabilities.CapabilityManager;
-import net.minecraftforge.common.util.LazyOptional;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-public final class CapabilityWiredElement
-{
- @CapabilityInject( IWiredElement.class )
- public static Capability CAPABILITY = null;
-
- private CapabilityWiredElement() {}
-
- public static void register()
- {
- CapabilityManager.INSTANCE.register( IWiredElement.class, new NullStorage(), NullElement::new );
- }
-
- private static class NullElement implements IWiredElement
- {
- @Nonnull
- @Override
- public IWiredNode getNode()
- {
- throw new IllegalStateException( "Should not use the default element implementation" );
- }
-
- @Nonnull
- @Override
- public World getWorld()
- {
- throw new IllegalStateException( "Should not use the default element implementation" );
- }
-
- @Nonnull
- @Override
- public Vec3d getPosition()
- {
- throw new IllegalStateException( "Should not use the default element implementation" );
- }
-
- @Nonnull
- @Override
- public String getSenderID()
- {
- throw new IllegalStateException( "Should not use the default element implementation" );
- }
- }
-
- private static class NullStorage implements Capability.IStorage
- {
- @Override
- public INBT writeNBT( Capability capability, IWiredElement instance, Direction side )
- {
- return null;
- }
-
- @Override
- public void readNBT( Capability capability, IWiredElement instance, Direction side, INBT base )
- {
- }
- }
-
- private static final IWiredElement NULL_ELEMENT = new NullElement();
-
- @Nullable
- public static IWiredElement unwrap( LazyOptional capability )
- {
- IWiredElement element = capability.orElse( NULL_ELEMENT );
- return element == NULL_ELEMENT ? null : element;
- }
-}