From 737b3cb57696fb5517252e7db38bc88ce960b4d8 Mon Sep 17 00:00:00 2001 From: SquidDev Date: Thu, 10 Dec 2020 19:02:08 +0000 Subject: [PATCH] Don't use capabilities for generic peripherals Maybe the capability system was a mistake in retrospect, as we don't store the peripheral outside, so there's no way to reuse it. That will probably come in a later change. As a smaller fix, we pass the invalidate listener directly. The lifetime of this is the same as the computer, so we don't create a new one each time. There's still the potential to leak memory if people break/replace a computer (as listeners aren't removed), but that's an unavoidable flaw with capabilities. Fixes #593 --- .../computercraft/shared/Peripherals.java | 2 +- .../computer/blocks/TileComputerBase.java | 13 ++++++++++++- .../generic/GenericPeripheralProvider.java | 19 +++++++++++++------ 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/main/java/dan200/computercraft/shared/Peripherals.java b/src/main/java/dan200/computercraft/shared/Peripherals.java index cc61e4451..1d93147b4 100644 --- a/src/main/java/dan200/computercraft/shared/Peripherals.java +++ b/src/main/java/dan200/computercraft/shared/Peripherals.java @@ -67,7 +67,7 @@ private static IPeripheral getPeripheralAt( World world, BlockPos pos, Direction } } - return CapabilityUtil.unwrap( GenericPeripheralProvider.getPeripheral( world, pos, side ), invalidate ); + return GenericPeripheralProvider.getPeripheral( world, pos, side, invalidate ); } } 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 b384810b9..e904e02f4 100644 --- a/src/main/java/dan200/computercraft/shared/computer/blocks/TileComputerBase.java +++ b/src/main/java/dan200/computercraft/shared/computer/blocks/TileComputerBase.java @@ -39,6 +39,8 @@ import net.minecraft.util.text.StringTextComponent; import net.minecraft.util.text.TranslationTextComponent; 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; @@ -56,6 +58,7 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT private boolean m_on = false; boolean m_startOn = false; private boolean m_fresh = false; + private final NonNullConsumer>[] invalidate; private final ComputerFamily family; @@ -63,6 +66,14 @@ public TileComputerBase( TileEntityType type, ComputerFam { super( type ); this.family = family; + + // We cache these so we can guarantee we only ever register one listener for adjacent capabilities. + @SuppressWarnings( { "unchecked", "rawtypes" } ) + NonNullConsumer>[] invalidate = this.invalidate = new NonNullConsumer[6]; + for( Direction direction : Direction.values() ) + { + invalidate[direction.ordinal()] = o -> updateInput( direction ); + } } protected void unload() @@ -225,7 +236,7 @@ private void updateSideInput( ServerComputer computer, Direction dir, BlockPos o computer.setBundledRedstoneInput( localDir, BundledRedstone.getOutput( getWorld(), offset, offsetSide ) ); if( !isPeripheralBlockedOnSide( localDir ) ) { - IPeripheral peripheral = Peripherals.getPeripheral( getWorld(), offset, offsetSide, o -> updateInput( dir ) ); + IPeripheral peripheral = Peripherals.getPeripheral( getWorld(), offset, offsetSide, invalidate[dir.ordinal()] ); computer.setPeripheral( localDir, peripheral ); } } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheralProvider.java b/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheralProvider.java index 149179b84..260f8d781 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheralProvider.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheralProvider.java @@ -15,11 +15,13 @@ import net.minecraft.world.World; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.util.LazyOptional; +import net.minecraftforge.common.util.NonNullConsumer; import net.minecraftforge.energy.CapabilityEnergy; import net.minecraftforge.fluids.capability.CapabilityFluidHandler; import net.minecraftforge.items.CapabilityItemHandler; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.util.ArrayList; import java.util.List; @@ -31,14 +33,13 @@ public class GenericPeripheralProvider CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, }; - @Nonnull - public static LazyOptional getPeripheral( @Nonnull World world, @Nonnull BlockPos pos, @Nonnull Direction side ) + @Nullable + public static IPeripheral getPeripheral( @Nonnull World world, @Nonnull BlockPos pos, @Nonnull Direction side, NonNullConsumer> invalidate ) { TileEntity tile = world.getTileEntity( pos ); - if( tile == null ) return LazyOptional.empty(); + if( tile == null ) return null; ArrayList saturated = new ArrayList<>( 0 ); - LazyOptional peripheral = LazyOptional.of( () -> new GenericPeripheral( tile, saturated ) ); List> tileMethods = PeripheralMethod.GENERATOR.getMethods( tile.getClass() ); if( !tileMethods.isEmpty() ) addSaturated( saturated, tile, tileMethods ); @@ -51,11 +52,11 @@ public static LazyOptional getPeripheral( @Nonnull World world, @No if( capabilityMethods.isEmpty() ) return; addSaturated( saturated, contents, capabilityMethods ); - wrapper.addListener( x -> peripheral.invalidate() ); + wrapper.addListener( cast( invalidate ) ); } ); } - return saturated.isEmpty() ? LazyOptional.empty() : peripheral; + return saturated.isEmpty() ? null : new GenericPeripheral( tile, saturated ); } private static void addSaturated( ArrayList saturated, Object target, List> methods ) @@ -66,4 +67,10 @@ private static void addSaturated( ArrayList saturated, Object t saturated.add( new SaturatedMethod( target, method ) ); } } + + @SuppressWarnings( { "unchecked", "rawtypes" } ) + private static NonNullConsumer cast( NonNullConsumer consumer ) + { + return (NonNullConsumer) consumer; + } }