1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-11-26 09:54:49 +00:00

Merge branch 'mc-1.16.x' into mc-1.17.x

This commit is contained in:
Jonathan Coates
2021-11-29 19:40:05 +00:00
44 changed files with 1306 additions and 353 deletions

View File

@@ -38,13 +38,13 @@ public final class Peripherals
}
@Nullable
public static IPeripheral getPeripheral( Level world, BlockPos pos, Direction side, NonNullConsumer<LazyOptional<IPeripheral>> invalidate )
public static IPeripheral getPeripheral( Level world, BlockPos pos, Direction side, NonNullConsumer<Object> invalidate )
{
return world.isInWorldBounds( pos ) && !world.isClientSide ? getPeripheralAt( world, pos, side, invalidate ) : null;
}
@Nullable
private static IPeripheral getPeripheralAt( Level world, BlockPos pos, Direction side, NonNullConsumer<LazyOptional<IPeripheral>> invalidate )
private static IPeripheral getPeripheralAt( Level world, BlockPos pos, Direction side, NonNullConsumer<? super Object> invalidate )
{
BlockEntity block = world.getBlockEntity( pos );
if( block != null )

View File

@@ -59,7 +59,7 @@ public abstract class BlockComputerBase<T extends TileComputerBase> extends Bloc
super.onPlace( state, world, pos, oldState, isMoving );
BlockEntity tile = world.getBlockEntity( pos );
if( tile instanceof TileComputerBase computer ) computer.updateInput();
if( tile instanceof TileComputerBase computer ) computer.updateInputsImmediately( );
}
@Override

View File

@@ -31,14 +31,9 @@ import net.minecraft.world.Nameable;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.DiodeBlock;
import net.minecraft.world.level.block.RedStoneWireBlock;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.common.util.NonNullConsumer;
import javax.annotation.Nonnull;
@@ -57,7 +52,9 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
private boolean on = false;
boolean startOn = false;
private boolean fresh = false;
private final NonNullConsumer<LazyOptional<IPeripheral>>[] invalidate;
private int invalidSides = 0;
private final NonNullConsumer<Object>[] invalidate;
private final ComputerFamily family;
@@ -68,10 +65,11 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
// 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];
NonNullConsumer<Object>[] invalidate = this.invalidate = new NonNullConsumer[6];
for( Direction direction : Direction.values() )
{
invalidate[direction.ordinal()] = o -> updateInput( direction );
int mask = 1 << direction.ordinal();
invalidate[direction.ordinal()] = o -> invalidSides |= mask;
}
}
@@ -143,19 +141,26 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
@Override
public void onNeighbourChange( @Nonnull BlockPos neighbour )
{
updateInput( neighbour );
updateInputAt( neighbour );
}
@Override
public void onNeighbourTileEntityChange( @Nonnull BlockPos neighbour )
{
updateInput( neighbour );
updateInputAt( neighbour );
}
protected void serverTick()
{
ServerComputer computer = createServerComputer();
if( computer == null ) return;
if( invalidSides != 0 )
{
for( Direction direction : DirectionUtil.FACINGS )
{
if( (invalidSides & (1 << direction.ordinal())) != 0 ) refreshPeripheral( computer, direction );
}
}
// If the computer isn't on and should be, then turn it on
if( startOn || (fresh && on) )
@@ -222,89 +227,80 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
return localSide;
}
private void updateSideInput( ServerComputer computer, Direction dir, BlockPos offset )
private void updateRedstoneInput( @Nonnull ServerComputer computer, Direction dir, BlockPos targetPos )
{
Direction offsetSide = dir.getOpposite();
ComputerSide localDir = remapToLocalSide( dir );
computer.setRedstoneInput( localDir, getRedstoneInput( level, offset, dir ) );
computer.setBundledRedstoneInput( localDir, BundledRedstone.getOutput( getLevel(), offset, offsetSide ) );
if( !isPeripheralBlockedOnSide( localDir ) )
{
IPeripheral peripheral = Peripherals.getPeripheral( getLevel(), offset, offsetSide, invalidate[dir.ordinal()] );
computer.setPeripheral( localDir, peripheral );
}
computer.setRedstoneInput( localDir, RedstoneUtil.getRedstoneInput( level, targetPos, dir ) );
computer.setBundledRedstoneInput( localDir, BundledRedstone.getOutput( getLevel(), targetPos, offsetSide ) );
}
private void refreshPeripheral( @Nonnull ServerComputer computer, Direction dir )
{
invalidSides &= ~(1 << dir.ordinal());
ComputerSide localDir = remapToLocalSide( dir );
if( isPeripheralBlockedOnSide( localDir ) ) return;
Direction offsetSide = dir.getOpposite();
IPeripheral peripheral = Peripherals.getPeripheral( getLevel(), getBlockPos().relative( dir ), offsetSide, invalidate[dir.ordinal()] );
computer.setPeripheral( localDir, peripheral );
}
public void updateInputsImmediately()
{
ServerComputer computer = getServerComputer();
if( computer != null ) updateInputsImmediately( computer );
}
/**
* Gets the redstone input for an adjacent block.
* Update all redstone and peripherals.
*
* @param world The world we exist in
* @param pos The position of the neighbour
* @param side The side we are reading from
* @return The effective redstone power
* @see DiodeBlock#getInputSignal(Level, BlockPos, BlockState)
* This should only be really be called when the computer is being ticked (though there are some cases where it
* won't be), as peripheral scanning requires adjacent tiles to be in a "correct" state - which may not be the case
* if they're still updating!
*
* @param computer The current computer instance.
*/
protected static int getRedstoneInput( Level world, BlockPos pos, Direction side )
private void updateInputsImmediately( @Nonnull ServerComputer computer )
{
int power = world.getSignal( pos, side );
if( power >= 15 ) return power;
BlockState neighbour = world.getBlockState( pos );
return neighbour.getBlock() == Blocks.REDSTONE_WIRE
? Math.max( power, neighbour.getValue( RedStoneWireBlock.POWER ) )
: power;
}
public void updateInput()
{
if( getLevel() == null || getLevel().isClientSide ) return;
// Update all sides
ServerComputer computer = getServerComputer();
if( computer == null ) return;
BlockPos pos = computer.getPosition();
BlockPos pos = getBlockPos();
for( Direction dir : DirectionUtil.FACINGS )
{
updateSideInput( computer, dir, pos.relative( dir ) );
updateRedstoneInput( computer, dir, pos.relative( dir ) );
refreshPeripheral( computer, dir );
}
}
private void updateInput( BlockPos neighbour )
private void updateInputAt( @Nonnull BlockPos neighbour )
{
if( getLevel() == null || getLevel().isClientSide ) return;
ServerComputer computer = getServerComputer();
if( computer == null ) return;
for( Direction dir : DirectionUtil.FACINGS )
{
BlockPos offset = worldPosition.relative( dir );
BlockPos offset = getBlockPos().relative( dir );
if( offset.equals( neighbour ) )
{
updateSideInput( computer, dir, offset );
updateRedstoneInput( computer, dir, offset );
invalidSides |= 1 << dir.ordinal();
return;
}
}
// If the position is not any adjacent one, update all inputs.
updateInput();
}
private void updateInput( Direction dir )
{
if( getLevel() == null || getLevel().isClientSide ) return;
ServerComputer computer = getServerComputer();
if( computer == null ) return;
updateSideInput( computer, dir, worldPosition.relative( dir ) );
// If the position is not any adjacent one, update all inputs. This is pretty terrible, but some redstone mods
// handle this incorrectly.
BlockPos pos = getBlockPos();
for( Direction dir : DirectionUtil.FACINGS ) updateRedstoneInput( computer, dir, pos.relative( dir ) );
invalidSides = (1 << 6) - 1; // Mark all peripherals as dirty.
}
/**
* Update the block's state and propagate redstone output.
*/
public void updateOutput()
{
// Update redstone
updateBlock();
for( Direction dir : DirectionUtil.FACINGS )
{
@@ -354,9 +350,10 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
return family;
}
@Nonnull
public ServerComputer createServerComputer()
{
if( getLevel().isClientSide ) return null;
if( getLevel().isClientSide ) throw new IllegalStateException( "Cannot access server computer on the client." );
boolean changed = false;
if( instanceID < 0 )
@@ -364,18 +361,21 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
instanceID = ComputerCraft.serverComputerRegistry.getUnusedInstanceID();
changed = true;
}
if( !ComputerCraft.serverComputerRegistry.contains( instanceID ) )
ServerComputer computer = ComputerCraft.serverComputerRegistry.get( instanceID );
if( computer == null )
{
ServerComputer computer = createComputer( instanceID, computerID );
computer = createComputer( instanceID, computerID );
ComputerCraft.serverComputerRegistry.add( instanceID, computer );
fresh = true;
changed = true;
}
if( changed ) updateInput();
return ComputerCraft.serverComputerRegistry.get( instanceID );
if( changed ) updateInputsImmediately( computer );
return computer;
}
@Nullable
public ServerComputer getServerComputer()
{
return getLevel().isClientSide ? null : ComputerCraft.serverComputerRegistry.get( instanceID );

View File

@@ -128,7 +128,7 @@ public class CommandBlockPeripheral implements IPeripheral, ICapabilityProvider
public static void onCapability( AttachCapabilitiesEvent<BlockEntity> event )
{
BlockEntity tile = event.getObject();
if( tile instanceof CommandBlockEntity )
if( ComputerCraft.enableCommandBlock && tile instanceof CommandBlockEntity )
{
CommandBlockPeripheral peripheral = new CommandBlockPeripheral( (CommandBlockEntity) tile );
event.addCapability( CAP_ID, peripheral );

View File

@@ -18,18 +18,21 @@ import net.minecraft.world.level.block.entity.BlockEntity;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Set;
class GenericPeripheral implements IDynamicPeripheral
{
private final String type;
private final Set<String> additionalTypes;
private final BlockEntity tile;
private final List<SaturatedMethod> methods;
GenericPeripheral( BlockEntity tile, String name, List<SaturatedMethod> methods )
GenericPeripheral( BlockEntity tile, String name, Set<String> additionalTypes, List<SaturatedMethod> methods )
{
ResourceLocation type = tile.getType().getRegistryName();
this.tile = tile;
this.type = name != null ? name : (type != null ? type.toString() : "unknown");
this.additionalTypes = additionalTypes;
this.methods = methods;
}
@@ -56,6 +59,13 @@ class GenericPeripheral implements IDynamicPeripheral
return type;
}
@Nonnull
@Override
public Set<String> getAdditionalTypes()
{
return additionalTypes;
}
@Nullable
@Override
public Object getTarget()

View File

@@ -9,6 +9,7 @@ import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.peripheral.PeripheralType;
import dan200.computercraft.core.asm.NamedMethod;
import dan200.computercraft.core.asm.PeripheralMethod;
import dan200.computercraft.shared.util.CapabilityUtil;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.Level;
@@ -19,9 +20,7 @@ import net.minecraftforge.common.util.NonNullConsumer;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.*;
public class GenericPeripheralProvider
{
@@ -34,7 +33,7 @@ public class GenericPeripheralProvider
}
@Nullable
public static IPeripheral getPeripheral( @Nonnull Level world, @Nonnull BlockPos pos, @Nonnull Direction side, NonNullConsumer<LazyOptional<IPeripheral>> invalidate )
public static IPeripheral getPeripheral( @Nonnull Level world, @Nonnull BlockPos pos, @Nonnull Direction side, NonNullConsumer<Object> invalidate )
{
BlockEntity tile = world.getBlockEntity( pos );
if( tile == null ) return null;
@@ -52,7 +51,7 @@ public class GenericPeripheralProvider
if( capabilityMethods.isEmpty() ) return;
saturated.addMethods( contents, capabilityMethods );
wrapper.addListener( cast( invalidate ) );
CapabilityUtil.addListener( wrapper, invalidate );
} );
}
@@ -61,15 +60,16 @@ public class GenericPeripheralProvider
private static class GenericPeripheralBuilder
{
String name;
final ArrayList<SaturatedMethod> methods = new ArrayList<>( 0 );
private String name;
private final Set<String> additionalTypes = new HashSet<>( 0 );
private final ArrayList<SaturatedMethod> methods = new ArrayList<>( 0 );
IPeripheral toPeripheral( BlockEntity tile )
{
if( methods.isEmpty() ) return null;
methods.trimToSize();
return new GenericPeripheral( tile, name, methods );
return new GenericPeripheral( tile, name, additionalTypes, methods );
}
void addMethods( Object target, List<NamedMethod<PeripheralMethod>> methods )
@@ -88,13 +88,8 @@ public class GenericPeripheralProvider
String name = type.getPrimaryType();
if( this.name == null || this.name.compareTo( name ) > 0 ) this.name = name;
}
if( type != null ) additionalTypes.addAll( type.getAdditionalTypes() );
}
}
}
@SuppressWarnings( { "unchecked", "rawtypes" } )
private static <T> NonNullConsumer<T> cast( NonNullConsumer<?> consumer )
{
return (NonNullConsumer) consumer;
}
}

View File

@@ -6,8 +6,9 @@
package dan200.computercraft.shared.peripheral.generic.methods;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.lua.GenericSource;
import dan200.computercraft.api.lua.LuaFunction;
import dan200.computercraft.api.peripheral.GenericPeripheral;
import dan200.computercraft.api.peripheral.PeripheralType;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.energy.IEnergyStorage;
@@ -25,8 +26,15 @@ import javax.annotation.Nonnull;
*
* @cc.module energy_storage
*/
public class EnergyMethods implements GenericSource
public class EnergyMethods implements GenericPeripheral
{
@Nonnull
@Override
public PeripheralType getType()
{
return PeripheralType.ofAdditional( "energy_storage" );
}
@Nonnull
@Override
public ResourceLocation id()

View File

@@ -6,13 +6,15 @@
package dan200.computercraft.shared.peripheral.generic.methods;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.lua.GenericSource;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.lua.LuaFunction;
import dan200.computercraft.api.peripheral.GenericPeripheral;
import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.peripheral.PeripheralType;
import dan200.computercraft.shared.peripheral.generic.data.FluidData;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.material.Fluid;
import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.common.util.LazyOptional;
@@ -34,8 +36,15 @@ import static dan200.computercraft.shared.peripheral.generic.methods.ArgumentHel
*
* @cc.module fluid_storage
*/
public class FluidMethods implements GenericSource
public class FluidMethods implements GenericPeripheral
{
@Nonnull
@Override
public PeripheralType getType()
{
return PeripheralType.ofAdditional( "fluid_storage" );
}
@Nonnull
@Override
public ResourceLocation id()
@@ -155,13 +164,15 @@ public class FluidMethods implements GenericSource
@Nullable
private static IFluidHandler extractHandler( @Nullable Object object )
{
if( object instanceof ICapabilityProvider )
if( object instanceof BlockEntity blockEntity && blockEntity.isRemoved() ) return null;
if( object instanceof ICapabilityProvider provider )
{
LazyOptional<IFluidHandler> cap = ((ICapabilityProvider) object).getCapability( CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY );
LazyOptional<IFluidHandler> cap = provider.getCapability( CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY );
if( cap.isPresent() ) return cap.orElseThrow( NullPointerException::new );
}
if( object instanceof IFluidHandler ) return (IFluidHandler) object;
if( object instanceof IFluidHandler handler ) return handler;
return null;
}

View File

@@ -6,16 +6,18 @@
package dan200.computercraft.shared.peripheral.generic.methods;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.lua.GenericSource;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.lua.LuaFunction;
import dan200.computercraft.api.peripheral.GenericPeripheral;
import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.peripheral.PeripheralType;
import dan200.computercraft.shared.peripheral.generic.data.ItemData;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.Container;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.items.CapabilityItemHandler;
@@ -36,8 +38,15 @@ import static dan200.computercraft.shared.peripheral.generic.methods.ArgumentHel
*
* @cc.module inventory
*/
public class InventoryMethods implements GenericSource
public class InventoryMethods implements GenericPeripheral
{
@Nonnull
@Override
public PeripheralType getType()
{
return PeripheralType.ofAdditional( "inventory" );
}
@Nonnull
@Override
public ResourceLocation id()
@@ -259,14 +268,16 @@ public class InventoryMethods implements GenericSource
@Nullable
private static IItemHandler extractHandler( @Nullable Object object )
{
if( object instanceof ICapabilityProvider )
if( object instanceof BlockEntity blockEntity && blockEntity.isRemoved() ) return null;
if( object instanceof ICapabilityProvider provider )
{
LazyOptional<IItemHandler> cap = ((ICapabilityProvider) object).getCapability( CapabilityItemHandler.ITEM_HANDLER_CAPABILITY );
LazyOptional<IItemHandler> cap = provider.getCapability( CapabilityItemHandler.ITEM_HANDLER_CAPABILITY );
if( cap.isPresent() ) return cap.orElseThrow( NullPointerException::new );
}
if( object instanceof IItemHandler ) return (IItemHandler) object;
if( object instanceof Container ) return new InvWrapper( (Container) object );
if( object instanceof IItemHandler handler ) return handler;
if( object instanceof Container container ) return new InvWrapper( container );
return null;
}

View File

@@ -77,8 +77,9 @@ public class TileCable extends TileGeneric
}
}
private boolean invalidPeripheral;
private boolean peripheralAccessAllowed;
private final WiredModemLocalPeripheral peripheral = new WiredModemLocalPeripheral( this::refreshPeripheral );
private final WiredModemLocalPeripheral peripheral = new WiredModemLocalPeripheral( this::queueRefreshPeripheral );
private boolean destroyed = false;
@@ -234,12 +235,20 @@ public class TileCable extends TileGeneric
if( !level.isClientSide && peripheralAccessAllowed )
{
Direction facing = getDirection();
if( getBlockPos().relative( facing ).equals( neighbour ) ) refreshPeripheral();
if( getBlockPos().relative( facing ).equals( neighbour ) ) queueRefreshPeripheral();
}
}
private void queueRefreshPeripheral()
{
if( invalidPeripheral ) return;
invalidPeripheral = true;
TickScheduler.schedule( this );
}
private void refreshPeripheral()
{
invalidPeripheral = false;
if( level != null && !isRemoved() && peripheral.attach( level, getBlockPos(), getDirection() ) )
{
updateConnectedPeripherals();
@@ -310,6 +319,8 @@ public class TileCable extends TileGeneric
{
if( getLevel().isClientSide ) return;
if( invalidPeripheral ) refreshPeripheral();
if( modem.getModemState().pollChanged() ) updateBlockState();
if( !connectionsFormed )

View File

@@ -108,13 +108,15 @@ public class TileWiredModemFull extends TileGeneric
private final NonNullConsumer<LazyOptional<IWiredElement>> connectedNodeChanged = x -> connectionsChanged();
private int invalidSides = 0;
public TileWiredModemFull( BlockEntityType<TileWiredModemFull> type, BlockPos pos, BlockState state )
{
super( type, pos, state );
for( int i = 0; i < peripherals.length; i++ )
{
Direction facing = Direction.from3DDataValue( i );
peripherals[i] = new WiredModemLocalPeripheral( () -> refreshPeripheral( facing ) );
peripherals[i] = new WiredModemLocalPeripheral( () -> queueRefreshPeripheral( facing ) );
}
}
@@ -173,13 +175,20 @@ public class TileWiredModemFull extends TileGeneric
{
for( Direction facing : DirectionUtil.FACINGS )
{
if( getBlockPos().relative( facing ).equals( neighbour ) ) refreshPeripheral( facing );
if( getBlockPos().relative( facing ).equals( neighbour ) ) queueRefreshPeripheral( facing );
}
}
}
private void queueRefreshPeripheral( @Nonnull Direction facing )
{
if( invalidSides == 0 ) TickScheduler.schedule( this );
invalidSides |= 1 << facing.ordinal();
}
private void refreshPeripheral( @Nonnull Direction facing )
{
invalidSides &= ~(1 << facing.ordinal());
WiredModemLocalPeripheral peripheral = peripherals[facing.ordinal()];
if( level != null && !isRemoved() && peripheral.attach( level, getBlockPos(), facing ) )
{
@@ -262,6 +271,14 @@ public class TileWiredModemFull extends TileGeneric
{
if( getLevel().isClientSide ) return;
if( invalidSides != 0 )
{
for( Direction direction : DirectionUtil.FACINGS )
{
if( (invalidSides & (1 << direction.ordinal())) != 0 ) refreshPeripheral( direction );
}
}
if( modemState.pollChanged() ) updateBlockState();
if( !connectionsFormed )

View File

@@ -15,7 +15,6 @@ import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.common.util.NonNullConsumer;
import javax.annotation.Nonnull;
@@ -38,7 +37,7 @@ public final class WiredModemLocalPeripheral
private String type;
private IPeripheral peripheral;
private final NonNullConsumer<LazyOptional<IPeripheral>> invalidate;
private final NonNullConsumer<Object> invalidate;
public WiredModemLocalPeripheral( @Nonnull Runnable invalidate )
{

View File

@@ -21,6 +21,7 @@ import dan200.computercraft.core.apis.PeripheralAPI;
import dan200.computercraft.core.asm.PeripheralMethod;
import dan200.computercraft.shared.peripheral.modem.ModemPeripheral;
import dan200.computercraft.shared.peripheral.modem.ModemState;
import dan200.computercraft.shared.util.LuaUtil;
import net.minecraft.world.level.Level;
import javax.annotation.Nonnull;
@@ -118,13 +119,35 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
* @param name The peripheral's name.
* @return The peripheral's name.
* @cc.treturn string|nil The peripheral's type, or {@code nil} if it is not present.
* @cc.changed 1.99 Peripherals can have multiple types - this function returns multiple values.
* @see PeripheralAPI#getType
*/
@LuaFunction
public final Object[] getTypeRemote( IComputerAccess computer, String name )
{
RemotePeripheralWrapper wrapper = getWrapper( computer, name );
return wrapper != null ? new Object[] { wrapper.getType() } : null;
return wrapper == null ? null : LuaUtil.consArray( wrapper.getType(), wrapper.getAdditionalTypes() );
}
/**
* Check a peripheral is of a particular type.
*
* <blockquote><strong>Important:</strong> This function only appears on wired modems. Check {@link #isWireless}
* returns false before calling it.</blockquote>
*
* @param computer The calling computer.
* @param name The peripheral's name.
* @param type The type to check.
* @return The peripheral's name.
* @cc.treturn boolean|nil If a peripheral has a particular type, or {@literal nil} if it is not present.
* @cc.since 1.99
* @see PeripheralAPI#getType
*/
@LuaFunction
public final Object[] hasTypeRemote( IComputerAccess computer, String name, String type )
{
RemotePeripheralWrapper wrapper = getWrapper( computer, name );
return wrapper == null ? null : new Object[] { wrapper.getType().equals( type ) || wrapper.getAdditionalTypes().contains( getType() ) };
}
/**
@@ -307,6 +330,7 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
private final String name;
private final String type;
private final Set<String> additionalTypes;
private final Map<String, PeripheralMethod> methodMap;
private volatile boolean attached;
@@ -320,6 +344,7 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
this.name = name;
type = Objects.requireNonNull( peripheral.getType(), "Peripheral type cannot be null" );
additionalTypes = peripheral.getAdditionalTypes();
methodMap = PeripheralAPI.getMethods( peripheral );
}
@@ -353,6 +378,11 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
return type;
}
public Set<String> getAdditionalTypes()
{
return additionalTypes;
}
public Collection<String> getMethodNames()
{
return methodMap.keySet();

View File

@@ -331,8 +331,10 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default
{
if( dir.getAxis() == Direction.Axis.Y ) dir = Direction.NORTH;
level.setBlockAndUpdate( worldPosition, getBlockState().setValue( BlockTurtle.FACING, dir ) );
updateOutput();
updateInput();
updateInputsImmediately();
onTileEntityChange();
}

View File

@@ -332,16 +332,17 @@ public class TurtleBrain implements ITurtleAccess
// Copy the old turtle state into the new turtle
newTurtle.setLevel( world );
newTurtle.transferStateFrom( oldOwner );
newTurtle.createServerComputer().setLevel( world );
newTurtle.createServerComputer().setPosition( pos );
ServerComputer computer = newTurtle.createServerComputer();
computer.setLevel( world );
computer.setPosition( pos );
// Remove the old turtle
oldWorld.removeBlock( oldPos, false );
// Make sure everybody knows about it
newTurtle.updateBlock();
newTurtle.updateInput();
newTurtle.updateOutput();
newTurtle.updateInputsImmediately();
return true;
}
}
@@ -613,16 +614,16 @@ public class TurtleBrain implements ITurtleAccess
@Override
public void setUpgrade( @Nonnull TurtleSide side, ITurtleUpgrade upgrade )
{
if( !setUpgradeDirect( side, upgrade ) ) return;
if( !setUpgradeDirect( side, upgrade ) || owner.getLevel() == null ) return;
// This is a separate function to avoid updating the block when reading the NBT. We don't need to do this as
// either the block is newly placed (and so won't have changed) or is being updated with /data, which calls
// updateBlock for us.
if( owner.getLevel() != null )
{
owner.updateBlock();
owner.updateInput();
}
owner.updateBlock();
// Recompute peripherals in case an upgrade being removed has exposed a new peripheral.
// TODO: Only update peripherals, or even only two sides?
owner.updateInputsImmediately();
}
private boolean setUpgradeDirect( @Nonnull TurtleSide side, ITurtleUpgrade upgrade )
@@ -644,7 +645,7 @@ public class TurtleBrain implements ITurtleAccess
if( upgrade != null ) upgrades.put( side, upgrade );
// Notify clients and create peripherals
if( owner.getLevel() != null )
if( owner.getLevel() != null && !owner.getLevel().isClientSide )
{
updatePeripherals( owner.createServerComputer() );
}

View File

@@ -35,12 +35,20 @@ public final class CapabilityUtil
}
}
public static <T> void addListener( LazyOptional<T> p, NonNullConsumer<? super LazyOptional<T>> invalidate )
{
// We can make this safe with invalidate::accept, but then we're allocating it's just kind of absurd.
@SuppressWarnings( "unchecked" )
NonNullConsumer<LazyOptional<T>> safeInvalidate = (NonNullConsumer<LazyOptional<T>>) invalidate;
p.addListener( safeInvalidate );
}
@Nullable
public static <T> T unwrap( LazyOptional<T> p, NonNullConsumer<LazyOptional<T>> invalidate )
public static <T> T unwrap( LazyOptional<T> p, NonNullConsumer<? super LazyOptional<T>> invalidate )
{
if( !p.isPresent() ) return null;
p.addListener( invalidate );
addListener( p, invalidate );
return p.orElseThrow( NullPointerException::new );
}

View File

@@ -0,0 +1,23 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.util;
import java.util.Collection;
public class LuaUtil
{
public static Object[] consArray( Object value, Collection<?> rest )
{
if( rest.isEmpty() ) return new Object[] { value };
// I'm not proud of this code.
Object[] out = new Object[rest.size() + 1];
out[0] = value;
int i = 1;
for( Object additionalType : rest ) out[i++] = additionalType;
return out;
}
}

View File

@@ -8,6 +8,9 @@ package dan200.computercraft.shared.util;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.DiodeBlock;
import net.minecraft.world.level.block.RedStoneWireBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.event.ForgeEventFactory;
@@ -15,6 +18,30 @@ import java.util.EnumSet;
public final class RedstoneUtil
{
private RedstoneUtil()
{
}
/**
* Gets the redstone input for an adjacent block.
*
* @param world The world we exist in
* @param pos The position of the neighbour
* @param side The side we are reading from
* @return The effective redstone power
* @see DiodeBlock#getInputSignal(Level, BlockPos, BlockState)
*/
public static int getRedstoneInput( Level world, BlockPos pos, Direction side )
{
int power = world.getSignal( pos, side );
if( power >= 15 ) return power;
BlockState neighbour = world.getBlockState( pos );
return neighbour.getBlock() == Blocks.REDSTONE_WIRE
? Math.max( power, neighbour.getValue( RedStoneWireBlock.POWER ) )
: power;
}
public static void propagateRedstoneOutput( Level world, BlockPos pos, Direction side )
{
// Propagate ordinary output. See BlockRedstoneDiode.notifyNeighbors