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
This commit is contained in:
SquidDev 2020-12-10 19:02:08 +00:00
parent d83a68f3ff
commit 737b3cb576
3 changed files with 26 additions and 8 deletions

View File

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

View File

@ -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<LazyOptional<IPeripheral>>[] invalidate;
private final ComputerFamily family;
@ -63,6 +66,14 @@ public TileComputerBase( TileEntityType<? extends TileGeneric> 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<LazyOptional<IPeripheral>>[] 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 );
}
}

View File

@ -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<IPeripheral> 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<LazyOptional<IPeripheral>> invalidate )
{
TileEntity tile = world.getTileEntity( pos );
if( tile == null ) return LazyOptional.empty();
if( tile == null ) return null;
ArrayList<SaturatedMethod> saturated = new ArrayList<>( 0 );
LazyOptional<IPeripheral> peripheral = LazyOptional.of( () -> new GenericPeripheral( tile, saturated ) );
List<NamedMethod<PeripheralMethod>> tileMethods = PeripheralMethod.GENERATOR.getMethods( tile.getClass() );
if( !tileMethods.isEmpty() ) addSaturated( saturated, tile, tileMethods );
@ -51,11 +52,11 @@ public static LazyOptional<IPeripheral> 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<SaturatedMethod> saturated, Object target, List<NamedMethod<PeripheralMethod>> methods )
@ -66,4 +67,10 @@ private static void addSaturated( ArrayList<SaturatedMethod> saturated, Object t
saturated.add( new SaturatedMethod( target, method ) );
}
}
@SuppressWarnings( { "unchecked", "rawtypes" } )
private static <T> NonNullConsumer<T> cast( NonNullConsumer<?> consumer )
{
return (NonNullConsumer) consumer;
}
}