Expose peripherals as a capability

This registers IPeripheral as a capability. As a result, all (Minecraft
facing) functionality operates using LazyOptional<_>s instead.

Peripheral providers should now return a LazyOptional<IPeripheral> too.
Hopefully this will allow custom peripherals to mark themselves as
invalid (say, because a dependency has changed).

While peripheral providers are somewhat redundant, they still have their
usages. If a peripheral is applied to a large number of blocks (for
instance, all inventories) then using capabilities does incur some
memory overhead.

We also make the following changes based on the above:
 - Remove the default implementation for IWiredElement, migrating the
   definition to a common "Capabilities" class.

 - Remove IPeripheralTile - we'll exclusively use capabilities now.
   Absurdly this is the most complex change, as all TEs needed to be
   migrated too.

   I'm not 100% sure of the correctness of this changes so far - I've
   tested it pretty well, but blocks with more complex peripheral logic
   (wired/wireless modems and turtles) are still a little messy.

 - Remove the "command block" peripheral provider, attaching a
   capability instead.
This commit is contained in:
SquidDev 2020-05-15 17:09:12 +01:00
parent d5f82fa458
commit 5409d441b5
29 changed files with 567 additions and 407 deletions

View File

@ -114,11 +114,11 @@
</module>
<module name="ParameterName" />
<module name="StaticVariableName">
<property name="format" value="^[a-z][a-zA-Z0-9]*|CAPABILITY(_[A-Z]+)?$" />
<property name="format" value="^[a-z][a-zA-Z0-9]*|CAPABILITY(_[A-Z_]+)?$" />
<property name="applyToPrivate" value="false" />
</module>
<module name="StaticVariableName">
<property name="format" value="^(s_)?[a-z][a-zA-Z0-9]*|CAPABILITY(_[A-Z]+)?$" />
<property name="format" value="^(s_)?[a-z][a-zA-Z0-9]*|CAPABILITY(_[A-Z_]+)?$" />
<property name="applyToPrivate" value="true" />
</module>
<module name="TypeName" />

View File

@ -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<IWiredElement> 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 );
}
}

View File

@ -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 <em>not</em> 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}.

View File

@ -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<IPeripheral> getPeripheral( @Nonnull World world, @Nonnull BlockPos pos, @Nonnull Direction side );
}

View File

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

View File

@ -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<IPeripheral> CAPABILITY_PERIPHERAL = null;
@CapabilityInject( IWiredElement.class )
public static Capability<IWiredElement> CAPABILITY_WIRED_ELEMENT = null;
private Capabilities()
{
}
}

View File

@ -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<IPeripheralProvider> 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<LazyOptional<IPeripheral>> 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<LazyOptional<IPeripheral>> invalidate )
{
TileEntity block = world.getTileEntity( pos );
if( block != null )
{
LazyOptional<IPeripheral> 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<IPeripheral> peripheral = peripheralProvider.getPeripheral( world, pos, side );
if( peripheral.isPresent() ) return CapabilityUtil.unwrap( peripheral, invalidate );
}
catch( Exception e )
{

View File

@ -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();

View File

@ -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

View File

@ -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<TileComputerBase> get;
public ComputerProxy( Supplier<TileComputerBase> get )
{
this.get = get;
}
protected TileComputerBase getTile()
{
return get.get();
}
public void turnOn()
{

View File

@ -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<TileComputer> 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<IPeripheral> 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 <T> LazyOptional<T> getCapability( @Nonnull Capability<T> 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 );
}
}

View File

@ -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()

View File

@ -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<IPeripheral> self;
public CommandBlockPeripheral( CommandBlockTileEntity commandBlock )
{
@ -53,4 +71,33 @@ public boolean equals( IPeripheral other )
{
return other != null && other.getClass() == getClass();
}
@Nonnull
@Override
public <T> LazyOptional<T> getCapability( @Nonnull Capability<T> 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<TileEntity> event )
{
TileEntity tile = event.getObject();
if( tile instanceof CommandBlockTileEntity )
{
CommandBlockPeripheral peripheral = new CommandBlockPeripheral( (CommandBlockTileEntity) tile );
event.addCapability( CAP_ID, peripheral );
event.addListener( peripheral::invalidate );
}
}
}

View File

@ -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<IItemHandlerModifiable> itemHandlerCap;
private LazyOptional<IPeripheral> 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 <T> LazyOptional<T> getCapability( @Nonnull Capability<T> 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 );
}

View File

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

View File

@ -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<TileCable> 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<IPeripheral> modemCap;
private final NonNullConsumer<LazyOptional<IWiredElement>> 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 <T> LazyOptional<T> getCapability( @Nonnull Capability<T> capability, @Nullable Direction facing )
public <T> LazyOptional<T> getCapability( @Nonnull Capability<T> 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()

View File

@ -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<TileWiredModemFull> 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<IPeripheral> 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 <T> LazyOptional<T> getCapability( @Nonnull Capability<T> capability, @Nullable Direction facing )
public <T> LazyOptional<T> getCapability( @Nonnull Capability<T> 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 );
}
};
}
}

View File

@ -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<LazyOptional<IPeripheral>> 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;
}
}

View File

@ -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<TileWirelessModem> 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<IPeripheral> 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 <T> LazyOptional<T> getCapability( @Nonnull Capability<T> 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 );
}
}

View File

@ -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<TileMonitor> 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<IPeripheral> peripheralCap;
private final Set<IComputerAccess> 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 <T> LazyOptional<T> getCapability( @Nonnull Capability<T> 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;

View File

@ -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<TilePrinter> 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<ItemStack> m_inventory = NonNullList.withSize( SLOTS, ItemStack.EMPTY );
private LazyOptional<IItemHandlerModifiable>[] itemHandlerCaps;
private final SidedCaps<IItemHandler> itemHandlerCaps =
SidedCaps.ofNullable( facing -> facing == null ? new InvWrapper( this ) : new SidedInvWrapper( this, facing ) );
private LazyOptional<IPeripheral> 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 <T> LazyOptional<T> getCapability( @Nonnull Capability<T> capability, @Nullable Direction facing )
{
if( capability == ITEM_HANDLER_CAPABILITY )
if( capability == ITEM_HANDLER_CAPABILITY ) return itemHandlerCaps.get( facing ).cast();
if( capability == CAPABILITY_PERIPHERAL )
{
LazyOptional<IItemHandlerModifiable>[] handlers = itemHandlerCaps;
if( handlers == null ) handlers = itemHandlerCaps = new LazyOptional[7];
int index = facing == null ? 0 : 1 + facing.getIndex();
LazyOptional<IItemHandlerModifiable> 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 );

View File

@ -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<IPeripheral> 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 <T> LazyOptional<T> getCapability( @Nonnull Capability<T> 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

View File

@ -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 )

View File

@ -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<IPeripheral> 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 <T> LazyOptional<T> getCapability( @Nonnull Capability<T> 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 );
}

View File

@ -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<TurtleCommandQueueEntry> m_commandQueue = new ArrayDeque<>();
private final Queue<TurtleCommandQueueEntry> m_commandQueue = new ArrayDeque<>();
private int m_commandsIssued = 0;
private Map<TurtleSide, ITurtleUpgrade> m_upgrades = new EnumMap<>( TurtleSide.class );
private Map<TurtleSide, IPeripheral> peripherals = new EnumMap<>( TurtleSide.class );
private Map<TurtleSide, CompoundNBT> m_upgradeNBTData = new EnumMap<>( TurtleSide.class );
private final Map<TurtleSide, ITurtleUpgrade> m_upgrades = new EnumMap<>( TurtleSide.class );
private final Map<TurtleSide, IPeripheral> peripherals = new EnumMap<>( TurtleSide.class );
private final Map<TurtleSide, CompoundNBT> 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;
}

View File

@ -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 <T> LazyOptional<T> invalidate( @Nullable LazyOptional<T> cap )
{
if( cap != null ) cap.invalidate();
return null;
}
public static <T> void invalidate( @Nullable LazyOptional<T>[] caps )
{
if( caps == null ) return;
for( int i = 0; i < caps.length; i++ )
{
LazyOptional<T> cap = caps[i];
if( cap != null ) cap.invalidate();
caps[i] = null;
}
}
@Nullable
public static <T> T unwrap( LazyOptional<T> p, NonNullConsumer<LazyOptional<T>> invalidate )
{
if( !p.isPresent() ) return null;
p.addListener( invalidate );
return p.orElseThrow( NullPointerException::new );
}
}

View File

@ -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<T> implements Capability.IStorage<T>
{
@Override
public INBT writeNBT( Capability<T> capability, T instance, Direction side )
{
return null;
}
@Override
public void readNBT( Capability<T> capability, T instance, Direction side, INBT base )
{
}
}

View File

@ -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 <T> The type of the produced capability.
*/
public final class SidedCaps<T>
{
private final Function<Direction, T> factory;
private final boolean allowNull;
private T[] values;
private LazyOptional<T>[] caps;
private SidedCaps( Function<Direction, T> factory, boolean allowNull )
{
this.factory = factory;
this.allowNull = allowNull;
}
public static <T> SidedCaps<T> ofNonNull( Function<Direction, T> factory )
{
return new SidedCaps<>( factory, false );
}
public static <T> SidedCaps<T> ofNullable( Function<Direction, T> factory )
{
return new SidedCaps<>( factory, true );
}
@SuppressWarnings( { "unchecked", "rawtypes" } )
public LazyOptional<T> get( @Nullable Direction direction )
{
if( direction == null && !allowNull ) return LazyOptional.empty();
int index = direction == null ? 6 : direction.ordinal();
LazyOptional<T>[] caps = this.caps;
if( caps == null )
{
caps = this.caps = new LazyOptional[allowNull ? 7 : 6];
values = (T[]) new Object[caps.length];
}
LazyOptional<T> 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 );
}
}

View File

@ -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<IWiredElement> 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<IWiredElement>
{
@Override
public INBT writeNBT( Capability<IWiredElement> capability, IWiredElement instance, Direction side )
{
return null;
}
@Override
public void readNBT( Capability<IWiredElement> capability, IWiredElement instance, Direction side, INBT base )
{
}
}
private static final IWiredElement NULL_ELEMENT = new NullElement();
@Nullable
public static IWiredElement unwrap( LazyOptional<IWiredElement> capability )
{
IWiredElement element = capability.orElse( NULL_ELEMENT );
return element == NULL_ELEMENT ? null : element;
}
}