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 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 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 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; - } -}