diff --git a/src/main/java/dan200/computercraft/client/render/RenderOverlayCable.java b/src/main/java/dan200/computercraft/client/render/RenderOverlayCable.java index 5ee9e430fe..138ad3fffe 100644 --- a/src/main/java/dan200/computercraft/client/render/RenderOverlayCable.java +++ b/src/main/java/dan200/computercraft/client/render/RenderOverlayCable.java @@ -54,8 +54,6 @@ public void drawHighlight( DrawBlockHighlightEvent event ) GlStateManager.depthMask( false ); GlStateManager.pushMatrix(); - EnumFacing direction = type != PeripheralType.Cable ? cable.getDirection() : null; - { EntityPlayer player = event.getPlayer(); double x = player.lastTickPosX + (player.posX - player.lastTickPosX) * event.getPartialTicks(); @@ -78,7 +76,7 @@ public void drawHighlight( DrawBlockHighlightEvent event ) for( EnumFacing facing : EnumFacing.VALUES ) { - if( direction == facing || BlockCable.isCable( world, pos.offset( facing ) ) ) + if( BlockCable.doesConnectVisually( state, world, pos, facing ) ) { flags |= 1 << facing.ordinal(); diff --git a/src/main/java/dan200/computercraft/shared/peripheral/common/BlockCable.java b/src/main/java/dan200/computercraft/shared/peripheral/common/BlockCable.java index 801f001cbc..f0e04a4db9 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/common/BlockCable.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/common/BlockCable.java @@ -11,7 +11,6 @@ import dan200.computercraft.shared.peripheral.PeripheralType; import dan200.computercraft.shared.peripheral.modem.TileCable; import dan200.computercraft.shared.util.WorldUtil; -import net.minecraft.block.Block; import net.minecraft.block.properties.PropertyBool; import net.minecraft.block.properties.PropertyEnum; import net.minecraft.block.state.BlockFaceShape; @@ -51,23 +50,6 @@ public static class Properties public static final PropertyBool DOWN = PropertyBool.create( "down" ); } - public static boolean isCable( IBlockAccess world, BlockPos pos ) - { - Block block = world.getBlockState( pos ).getBlock(); - if( block == ComputerCraft.Blocks.cable ) - { - switch( ComputerCraft.Blocks.cable.getPeripheralType( world, pos ) ) - { - case Cable: - case WiredModemWithCable: - { - return true; - } - } - } - return false; - } - // Members public BlockCable() @@ -175,20 +157,17 @@ public IBlockState getDefaultBlockState( PeripheralType type, EnumFacing placedS } } - private boolean doesConnect( IBlockState state, IBlockAccess world, BlockPos pos, EnumFacing dir ) + public static boolean canConnectIn( IBlockState state, EnumFacing direction ) { - if( state.getValue( Properties.CABLE ) == BlockCableCableVariant.NONE ) - { - return false; - } - else if( state.getValue( Properties.MODEM ).getFacing() == dir ) - { - return true; - } - else - { - return isCable( world, pos.offset( dir ) ); - } + return state.getValue( BlockCable.Properties.CABLE ) != BlockCableCableVariant.NONE + && state.getValue( BlockCable.Properties.MODEM ).getFacing() != direction; + } + + public static boolean doesConnectVisually( IBlockState state, IBlockAccess world, BlockPos pos, EnumFacing direction ) + { + if( state.getValue( Properties.CABLE ) == BlockCableCableVariant.NONE ) return false; + if( state.getValue( Properties.MODEM ).getFacing() == direction ) return true; + return ComputerCraft.getWiredElementAt( world, pos.offset( direction ), direction.getOpposite() ) != null; } @Nonnull @@ -196,12 +175,12 @@ else if( state.getValue( Properties.MODEM ).getFacing() == dir ) @Deprecated public IBlockState getActualState( @Nonnull IBlockState state, IBlockAccess world, BlockPos pos ) { - state = state.withProperty( Properties.NORTH, doesConnect( state, world, pos, EnumFacing.NORTH ) ); - state = state.withProperty( Properties.SOUTH, doesConnect( state, world, pos, EnumFacing.SOUTH ) ); - state = state.withProperty( Properties.EAST, doesConnect( state, world, pos, EnumFacing.EAST ) ); - state = state.withProperty( Properties.WEST, doesConnect( state, world, pos, EnumFacing.WEST ) ); - state = state.withProperty( Properties.UP, doesConnect( state, world, pos, EnumFacing.UP ) ); - state = state.withProperty( Properties.DOWN, doesConnect( state, world, pos, EnumFacing.DOWN ) ); + state = state.withProperty( Properties.NORTH, doesConnectVisually( state, world, pos, EnumFacing.NORTH ) ); + state = state.withProperty( Properties.SOUTH, doesConnectVisually( state, world, pos, EnumFacing.SOUTH ) ); + state = state.withProperty( Properties.EAST, doesConnectVisually( state, world, pos, EnumFacing.EAST ) ); + state = state.withProperty( Properties.WEST, doesConnectVisually( state, world, pos, EnumFacing.WEST ) ); + state = state.withProperty( Properties.UP, doesConnectVisually( state, world, pos, EnumFacing.UP ) ); + state = state.withProperty( Properties.DOWN, doesConnectVisually( state, world, pos, EnumFacing.DOWN ) ); if( state.getValue( Properties.CABLE ) != BlockCableCableVariant.NONE ) { @@ -345,7 +324,6 @@ public boolean removedByPlayer( @Nonnull IBlockState state, World world, @Nonnul if( WorldUtil.isVecInsideInclusive( bb, hit.hitVec.subtract( pos.getX(), pos.getY(), pos.getZ() ) ) ) { world.setBlockState( pos, state.withProperty( Properties.MODEM, BlockCableModemVariant.None ), 3 ); - cable.modemChanged(); item = PeripheralItemFactory.create( PeripheralType.WiredModem, null, 1 ); } else @@ -365,6 +343,7 @@ public boolean removedByPlayer( @Nonnull IBlockState state, World world, @Nonnul return super.removedByPlayer( state, world, pos, player, willHarvest ); } + @Nonnull @Override public ItemStack getPickBlock( @Nonnull IBlockState state, RayTraceResult hit, @Nonnull World world, @Nonnull BlockPos pos, EntityPlayer player ) { @@ -373,7 +352,7 @@ public ItemStack getPickBlock( @Nonnull IBlockState state, RayTraceResult hit, @ { TileCable cable = (TileCable) tile; PeripheralType type = getPeripheralType( state ); - + if( type == PeripheralType.WiredModemWithCable ) { if( hit == null || WorldUtil.isVecInsideInclusive( cable.getModemBounds(), hit.hitVec.subtract( pos.getX(), pos.getY(), pos.getZ() ) ) ) diff --git a/src/main/java/dan200/computercraft/shared/peripheral/modem/TileCable.java b/src/main/java/dan200/computercraft/shared/peripheral/modem/TileCable.java index c7aa49efb8..8bad648485 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/modem/TileCable.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/modem/TileCable.java @@ -8,14 +8,8 @@ import com.google.common.base.Objects; import dan200.computercraft.ComputerCraft; -import dan200.computercraft.api.filesystem.IMount; -import dan200.computercraft.api.filesystem.IWritableMount; -import dan200.computercraft.api.lua.ILuaContext; -import dan200.computercraft.api.lua.LuaException; -import dan200.computercraft.api.network.IPacketNetwork; -import dan200.computercraft.api.network.IPacketReceiver; -import dan200.computercraft.api.network.Packet; -import dan200.computercraft.api.peripheral.IComputerAccess; +import dan200.computercraft.api.network.wired.IWiredElement; +import dan200.computercraft.api.network.wired.IWiredNode; import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.shared.common.BlockGeneric; import dan200.computercraft.shared.peripheral.PeripheralType; @@ -24,11 +18,12 @@ import dan200.computercraft.shared.peripheral.common.PeripheralItemFactory; import dan200.computercraft.shared.util.IDAssigner; import dan200.computercraft.shared.util.PeripheralUtil; +import dan200.computercraft.api.network.wired.IWiredElementTile; +import net.minecraft.block.Block; import net.minecraft.block.state.IBlockState; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumFacing; import net.minecraft.util.NonNullList; import net.minecraft.util.math.AxisAlignedBB; @@ -38,13 +33,13 @@ import net.minecraft.world.World; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.io.File; -import java.util.*; +import java.util.Collections; +import java.util.List; +import java.util.Map; -import static dan200.computercraft.core.apis.ArgumentHelper.getString; - -public class TileCable extends TileModemBase - implements IPacketNetwork +public class TileCable extends TileModemBase implements IWiredElementTile { public static final double MIN = 0.375; public static final double MAX = 1 - MIN; @@ -59,33 +54,13 @@ public class TileCable extends TileModemBase new AxisAlignedBB( MAX, MIN, MIN, 1, MAX, MAX ), // East }; - // Statics - - private static class Peripheral extends ModemPeripheral + private static class CableElement extends WiredModemElement { - private TileCable m_entity; - - public Peripheral( TileCable entity ) - { - m_entity = entity; - } - - @Override - public boolean isInterdimensional() - { - return false; - } - - @Override - public double getRange() - { - return 256.0; - } + private final TileCable m_entity; - @Override - protected IPacketNetwork getNetwork() + private CableElement( TileCable m_entity ) { - return m_entity; + this.m_entity = m_entity; } @Nonnull @@ -99,168 +74,81 @@ public World getWorld() @Override public Vec3d getPosition() { - EnumFacing direction = m_entity.getCachedDirection(); - BlockPos pos = m_entity.getPos().offset( direction ); - return new Vec3d( pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5 ); + BlockPos pos = m_entity.getPos(); + return new Vec3d( (double) pos.getX() + 0.5, (double) pos.getY() + 0.5, (double) pos.getZ() + 0.5 ); } @Nonnull @Override - public String[] getMethodNames() - { - String[] methods = super.getMethodNames(); - String[] newMethods = new String[ methods.length + 5 ]; - System.arraycopy( methods, 0, newMethods, 0, methods.length ); - newMethods[ methods.length ] = "getNamesRemote"; - newMethods[ methods.length + 1 ] = "isPresentRemote"; - newMethods[ methods.length + 2 ] = "getTypeRemote"; - newMethods[ methods.length + 3 ] = "getMethodsRemote"; - newMethods[ methods.length + 4 ] = "callRemote"; - return newMethods; - } - - @Override - public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException - { - String[] methods = super.getMethodNames(); - switch( method - methods.length ) - { - case 0: - { - // getNamesRemote - synchronized( m_entity.m_peripheralsByName ) - { - int idx = 1; - Map table = new HashMap<>(); - for( String name : m_entity.m_peripheralWrappersByName.keySet() ) - { - table.put( idx++, name ); - } - return new Object[] { table }; - } - } - case 1: - { - // isPresentRemote - String type = m_entity.getTypeRemote( getString( arguments, 0 ) ); - return new Object[] { type != null }; - } - case 2: - { - // getTypeRemote - String type = m_entity.getTypeRemote( getString( arguments, 0 ) ); - if( type != null ) - { - return new Object[] { type }; - } - return null; - } - case 3: - { - // getMethodsRemote - String[] methodNames = m_entity.getMethodNamesRemote( getString( arguments, 0 ) ); - if( methodNames != null ) - { - Map table = new HashMap<>(); - for(int i=0; i getPeripherals() { - super.attach( computer ); - synchronized( m_entity.m_peripheralsByName ) - { - for (String periphName : m_entity.m_peripheralsByName.keySet()) - { - IPeripheral peripheral = m_entity.m_peripheralsByName.get( periphName ); - if( peripheral != null ) - { - m_entity.attachPeripheral( periphName, peripheral ); - } - } - } + IPeripheral peripheral = m_entity.getConnectedPeripheral(); + return peripheral != null + ? Collections.singletonMap( m_entity.getConnectedPeripheralName(), peripheral ) + : Collections.emptyMap(); } @Override - public synchronized void detach( @Nonnull IComputerAccess computer ) + protected void attachPeripheral( String name, IPeripheral peripheral ) { - synchronized( m_entity.m_peripheralsByName ) + if( !name.equals( m_entity.getConnectedPeripheralName() ) ) { - for (String periphName : m_entity.m_peripheralsByName.keySet()) - { - m_entity.detachPeripheral( periphName ); - } + ((WiredModemPeripheral) m_entity.m_modem).attachPeripheral( name, peripheral ); } - super.detach( computer ); } @Override - public boolean equals( IPeripheral other ) + protected void detachPeripheral( String name ) { - if( other instanceof Peripheral ) - { - Peripheral otherModem = (Peripheral)other; - return otherModem.m_entity == m_entity; - } - return false; + ((WiredModemPeripheral) m_entity.m_modem).detachPeripheral( name ); } } - private static int s_nextUniqueSearchID = 1; - // Members - private final Set m_receivers; - private final Queue m_transmitQueue; - private boolean m_peripheralAccessAllowed; private int m_attachedPeripheralID; - - private final Map m_peripheralsByName; - private Map m_peripheralWrappersByName; - private boolean m_peripheralsKnown; + private boolean m_destroyed; - - private int m_lastSearchID; private boolean m_hasDirection = false; - + private boolean m_connectionsFormed = false; + + private WiredModemElement m_cable; + private IWiredNode m_node; + public TileCable() { - m_receivers = new HashSet<>(); - m_transmitQueue = new LinkedList<>(); - m_peripheralAccessAllowed = false; m_attachedPeripheralID = -1; - - m_peripheralsByName = new HashMap<>(); - m_peripheralWrappersByName = new HashMap<>(); - m_peripheralsKnown = false; + m_destroyed = false; - - m_lastSearchID = 0; + } + + @Override + protected ModemPeripheral createPeripheral() + { + m_cable = new CableElement( this ); + m_node = m_cable.getNode(); + return new WiredModemPeripheral( m_cable ) + { + @Nonnull + @Override + public Vec3d getPosition() + { + BlockPos pos = getPos().offset( getCachedDirection() ); + return new Vec3d( pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5 ); + } + }; + } + + private void remove() + { + if( world == null || !world.isRemote ) + { + m_node.remove(); + m_connectionsFormed = false; + } } @Override @@ -269,11 +157,25 @@ public void destroy() if( !m_destroyed ) { m_destroyed = true; - networkChanged(); + remove(); } super.destroy(); } + @Override + public void onChunkUnload() + { + super.onChunkUnload(); + remove(); + } + + @Override + public void invalidate() + { + super.invalidate(); + remove(); + } + @Override public void onLoad() { @@ -373,17 +275,20 @@ public void onNeighbourChange() case WiredModem: { // Drop everything and remove block - ((BlockGeneric)getBlockType()).dropAllItems( getWorld(), getPos(), false ); + ((BlockGeneric) getBlockType()).dropAllItems( getWorld(), getPos(), false ); getWorld().setBlockToAir( getPos() ); - break; + + // This'll call #destroy(), so we don't need to reset the network here. + return; } case WiredModemWithCable: { // Drop the modem and convert to cable - ((BlockGeneric)getBlockType()).dropItem( getWorld(), getPos(), PeripheralItemFactory.create( PeripheralType.WiredModem, getLabel(), 1 ) ); + ((BlockGeneric) getBlockType()).dropItem( getWorld(), getPos(), PeripheralItemFactory.create( PeripheralType.WiredModem, getLabel(), 1 ) ); setLabel( null ); setBlockState( getBlockState().withProperty( BlockCable.Properties.MODEM, BlockCableModemVariant.None ) ); - if( modemChanged() ) networkChanged(); + networkChanged(); + break; } } @@ -392,10 +297,10 @@ public void onNeighbourChange() public AxisAlignedBB getModemBounds() { - return super.getBounds(); + return super.getBounds(); } - - public AxisAlignedBB getCableBounds() + + private AxisAlignedBB getCableBounds() { double xMin = 0.375; double yMin = 0.375; @@ -405,33 +310,35 @@ public AxisAlignedBB getCableBounds() double zMax = 0.625; BlockPos pos = getPos(); World world = getWorld(); - if( BlockCable.isCable( world, pos.west() ) ) + + IBlockState state = getBlockState(); + if( BlockCable.doesConnectVisually( state, world, pos, EnumFacing.WEST ) ) { xMin = 0.0; } - if( BlockCable.isCable( world, pos.east() ) ) + if( BlockCable.doesConnectVisually( state, world, pos, EnumFacing.EAST ) ) { xMax = 1.0; } - if( BlockCable.isCable( world, pos.down() ) ) + if( BlockCable.doesConnectVisually( state, world, pos, EnumFacing.DOWN ) ) { yMin = 0.0; } - if( BlockCable.isCable( world, pos.up() ) ) + if( BlockCable.doesConnectVisually( state, world, pos, EnumFacing.UP ) ) { yMax = 1.0; } - if( BlockCable.isCable( world, pos.north() ) ) + if( BlockCable.doesConnectVisually( state, world, pos, EnumFacing.NORTH ) ) { zMin = 0.0; } - if( BlockCable.isCable( world, pos.south() ) ) + if( BlockCable.doesConnectVisually( state, world, pos, EnumFacing.SOUTH ) ) { zMax = 1.0; } return new AxisAlignedBB( xMin, yMin, zMin, xMax, yMax, zMax ); } - + @Nonnull @Override public AxisAlignedBB getBounds() @@ -468,12 +375,13 @@ public void getCollisionBounds( @Nonnull List bounds ) if( type == PeripheralType.Cable || type == PeripheralType.WiredModemWithCable ) { bounds.add( BOX_CENTRE ); - BlockPos pos = getPos(); - for (EnumFacing facing : EnumFacing.VALUES) + + IBlockState state = getBlockState(); + for( EnumFacing facing : EnumFacing.VALUES ) { - if( BlockCable.isCable( getWorld(), pos.offset( facing ) ) ) + if( BlockCable.doesConnectVisually( state, world, pos, facing ) ) { - bounds.add( BOXES[ facing.ordinal() ] ); + bounds.add( BOXES[facing.ordinal()] ); } } } @@ -519,30 +427,24 @@ public boolean onActivate( EntityPlayer player, EnumFacing side, float hitX, flo } @Override - public void readFromNBT(NBTTagCompound nbttagcompound) + public void readFromNBT( NBTTagCompound nbttagcompound ) { // Read properties - super.readFromNBT(nbttagcompound); + super.readFromNBT( nbttagcompound ); m_peripheralAccessAllowed = nbttagcompound.getBoolean( "peripheralAccess" ); m_attachedPeripheralID = nbttagcompound.getInteger( "peripheralID" ); } @Nonnull @Override - public NBTTagCompound writeToNBT(NBTTagCompound nbttagcompound) + public NBTTagCompound writeToNBT( NBTTagCompound nbttagcompound ) { // Write properties - nbttagcompound = super.writeToNBT(nbttagcompound); + nbttagcompound = super.writeToNBT( nbttagcompound ); nbttagcompound.setBoolean( "peripheralAccess", m_peripheralAccessAllowed ); nbttagcompound.setInteger( "peripheralID", m_attachedPeripheralID ); return nbttagcompound; } - - @Override - protected ModemPeripheral createPeripheral() - { - return new Peripheral( this ); - } @Override protected void updateAnim() @@ -559,190 +461,55 @@ protected void updateAnim() setAnim( anim ); } - // IPeripheralTile - - @Override - public IPeripheral getPeripheral( EnumFacing side ) - { - if( getPeripheralType() != PeripheralType.Cable ) - { - return super.getPeripheral( side ); - } - return null; - } - @Override public void update() { super.update(); updateDirection(); if( !getWorld().isRemote ) - { - synchronized( m_peripheralsByName ) + { + if( !m_connectionsFormed ) { - if( !m_peripheralsKnown ) - { - findPeripherals(); - m_peripheralsKnown = true; - } + networkChanged(); + if( m_peripheralAccessAllowed ) m_node.invalidate(); + m_connectionsFormed = true; } - synchronized( m_transmitQueue ) - { - while( m_transmitQueue.peek() != null ) - { - PacketWrapper p = m_transmitQueue.remove(); - if( p != null ) - { - dispatchPacket( p ); - } - } - } - } - } - - // IPacketNetwork implementation - - @Override - public void addReceiver( @Nonnull IPacketReceiver receiver ) - { - synchronized( m_receivers ) - { - m_receivers.add( receiver ); - } - } - - @Override - public void removeReceiver( @Nonnull IPacketReceiver receiver ) - { - synchronized( m_receivers ) - { - m_receivers.remove( receiver ); - } - } - - @Override - public void transmitSameDimension( @Nonnull Packet packet, double range ) - { - synchronized( m_transmitQueue ) - { - m_transmitQueue.offer( new PacketWrapper( packet, range ) ); - } - } - - @Override - public void transmitInterdimensional( @Nonnull Packet packet ) - { - synchronized( m_transmitQueue ) - { - m_transmitQueue.offer( new PacketWrapper( packet, Double.MAX_VALUE ) ); } } - @Override - public boolean isWireless() - { - return false; - } - - private void attachPeripheral( String periphName, IPeripheral peripheral ) + public void networkChanged() { - if( !m_peripheralWrappersByName.containsKey( periphName ) ) - { - RemotePeripheralWrapper wrapper = new RemotePeripheralWrapper( peripheral, m_modem.getComputer(), periphName ); - m_peripheralWrappersByName.put( periphName, wrapper ); - wrapper.attach(); - } - } + if( getWorld().isRemote ) return; - private void detachPeripheral( String periphName ) - { - if( m_peripheralWrappersByName.containsKey( periphName ) ) - { - RemotePeripheralWrapper wrapper = m_peripheralWrappersByName.get( periphName ); - m_peripheralWrappersByName.remove( periphName ); - wrapper.detach(); - } - } + if( modemChanged() ) m_node.invalidate(); - private String getTypeRemote( String remoteName ) - { - synchronized( m_peripheralsByName ) - { - RemotePeripheralWrapper wrapper = m_peripheralWrappersByName.get( remoteName ); - if( wrapper != null ) - { - return wrapper.getType(); - } - } - return null; - } - - private String[] getMethodNamesRemote( String remoteName ) - { - synchronized( m_peripheralsByName ) - { - RemotePeripheralWrapper wrapper = m_peripheralWrappersByName.get( remoteName ); - if( wrapper != null ) - { - return wrapper.getMethodNames(); - } - } - return null; - } - - private Object[] callMethodRemote( String remoteName, ILuaContext context, String method, Object[] arguments ) throws LuaException, InterruptedException - { - RemotePeripheralWrapper wrapper; - synchronized( m_peripheralsByName ) - { - wrapper = m_peripheralWrappersByName.get( remoteName ); - } - if( wrapper != null ) + IBlockState state = getBlockState(); + World world = getWorld(); + BlockPos current = getPos(); + for( EnumFacing facing : EnumFacing.VALUES ) { - return wrapper.callMethod( context, method, arguments ); - } - throw new LuaException( "No peripheral: "+remoteName ); - } + if( !world.isBlockLoaded( pos ) ) continue; + + IWiredElement element = ComputerCraft.getWiredElementAt( world, current.offset( facing ), facing.getOpposite() ); + if( element == null ) continue; - public void networkChanged() - { - if( !getWorld().isRemote ) - { - if( !m_destroyed && getPeripheralType() != PeripheralType.WiredModem) + if( BlockCable.canConnectIn( state, facing ) ) { - // If this modem is alive, rebuild the network - searchNetwork( ( modem, distance ) -> - { - synchronized( modem.m_peripheralsByName ) - { - modem.m_peripheralsKnown = false; - } - } ); + // If we can connect to it then do so + m_node.connectTo( element.getNode() ); } - else + else if( m_node.getNetwork() == element.getNode().getNetwork() ) { - // If this modem is dead, rebuild the neighbours' networks - for( EnumFacing dir : EnumFacing.values() ) - { - BlockPos offset = getPos().offset( dir ); - if( offset.getY() >= 0 && offset.getY() < getWorld().getHeight() && BlockCable.isCable( getWorld(), offset ) ) - { - TileEntity tile = getWorld().getTileEntity( offset ); - if( tile != null && tile instanceof TileCable ) - { - TileCable modem = (TileCable)tile; - modem.networkChanged(); - } - } - } + // Otherwise if we're on the same network then attempt to void it. + m_node.disconnectFrom( element.getNode() ); } } } - public boolean modemChanged() + private boolean modemChanged() { if( getWorld().isRemote ) return false; - + boolean requiresUpdate = false; PeripheralType type = getPeripheralType(); @@ -758,220 +525,12 @@ public boolean modemChanged() markDirty(); updateAnim(); } - - return requiresUpdate; - } - - // private stuff - - // Packet sending - - private static class PacketWrapper - { - final Packet m_packet; - final double m_range; - - private PacketWrapper( Packet m_packet, double m_range ) - { - this.m_packet = m_packet; - this.m_range = m_range; - } - } - - private void dispatchPacket( final PacketWrapper packet ) - { - searchNetwork( ( modem, distance ) -> - { - if( distance <= packet.m_range) - { - modem.receivePacket( packet.m_packet, distance ); - } - } ); - } - - private void receivePacket( Packet packet, int distanceTravelled ) - { - synchronized( m_receivers ) - { - for (IPacketReceiver device : m_receivers) - { - device.receiveSameDimension( packet, distanceTravelled ); - } - } - } - - // Remote peripheral control - - private static class RemotePeripheralWrapper implements IComputerAccess - { - private IPeripheral m_peripheral; - private IComputerAccess m_computer; - private String m_name; - - private String m_type; - private String[] m_methods; - private Map m_methodMap; - - public RemotePeripheralWrapper( IPeripheral peripheral, IComputerAccess computer, String name ) - { - m_peripheral = peripheral; - m_computer = computer; - m_name = name; - - m_type = peripheral.getType(); - m_methods = peripheral.getMethodNames(); - assert( m_type != null ); - assert( m_methods != null ); - - m_methodMap = new HashMap<>(); - for( int i=0; i newPeripheralsByName = new HashMap<>(); - if( getPeripheralType() == PeripheralType.WiredModemWithCable ) - { - searchNetwork( ( modem, distance ) -> - { - if( modem != origin ) - { - IPeripheral peripheral = modem.getConnectedPeripheral(); - String periphName = modem.getConnectedPeripheralName(); - if( peripheral != null && periphName != null ) - { - newPeripheralsByName.put( periphName, peripheral ); - } - } - } ); - } - //System.out.println( newPeripheralsByName.size()+" peripherals discovered" ); - - // Detach all the old peripherals - Iterator it = m_peripheralsByName.keySet().iterator(); - while( it.hasNext() ) - { - String periphName = it.next(); - if( !newPeripheralsByName.containsKey( periphName ) ) - { - detachPeripheral( periphName ); - it.remove(); - } - } - - // Attach all the new peripherals - for( String periphName : newPeripheralsByName.keySet() ) - { - if( !m_peripheralsByName.containsKey( periphName ) ) - { - IPeripheral peripheral = newPeripheralsByName.get( periphName ); - if( peripheral != null ) - { - m_peripheralsByName.put( periphName, peripheral ); - if( isAttached() ) - { - attachPeripheral( periphName, peripheral ); - } - } - } - } - //System.out.println( m_peripheralsByName.size()+" connected" ); - } - } - - public void togglePeripheralAccess() + // private stuff + private void togglePeripheralAccess() { if( !m_peripheralAccessAllowed ) { @@ -986,11 +545,12 @@ public void togglePeripheralAccess() { m_peripheralAccessAllowed = false; } - updateAnim(); - networkChanged(); + + updateAnim(); + m_node.invalidate(); } - - public String getConnectedPeripheralName() + + private String getConnectedPeripheralName() { IPeripheral periph = getConnectedPeripheral(); if( periph != null ) @@ -998,16 +558,16 @@ public String getConnectedPeripheralName() String type = periph.getType(); if( m_attachedPeripheralID < 0 ) { - m_attachedPeripheralID = IDAssigner.getNextIDFromFile(new File( - ComputerCraft.getWorldDir(getWorld()), + m_attachedPeripheralID = IDAssigner.getNextIDFromFile( new File( + ComputerCraft.getWorldDir( getWorld() ), "computer/lastid_" + type + ".txt" - )); + ) ); } return type + "_" + m_attachedPeripheralID; } return null; } - + private IPeripheral getConnectedPeripheral() { if( m_peripheralAccessAllowed ) @@ -1016,84 +576,45 @@ private IPeripheral getConnectedPeripheral() { EnumFacing facing = getDirection(); BlockPos neighbour = getPos().offset( facing ); - return PeripheralUtil.getPeripheral( getWorld(), neighbour, facing.getOpposite() ); + IPeripheral peripheral = getPeripheral( getWorld(), neighbour, facing.getOpposite() ); + return peripheral == null || peripheral instanceof WiredModemPeripheral ? null : peripheral; } } return null; } - - // Generic network search stuff - - private interface ICableVisitor - { - void visit( TileCable modem, int distance ); - } - - private static class SearchLoc - { - public World world; - public BlockPos pos; - public int distanceTravelled; - } - - private static void enqueue( Queue queue, World world, BlockPos pos, int distanceTravelled ) + + public static IPeripheral getPeripheral( World world, BlockPos pos, EnumFacing facing ) { - int y = pos.getY(); - if( y >= 0 && y < world.getHeight() && BlockCable.isCable( world, pos ) ) - { - SearchLoc loc = new SearchLoc(); - loc.world = world; - loc.pos = pos; - loc.distanceTravelled = distanceTravelled; - queue.offer( loc ); - } + Block block = world.getBlockState( pos ).getBlock(); + if( block == ComputerCraft.Blocks.wiredModemFull || block == ComputerCraft.Blocks.cable ) return null; + + return PeripheralUtil.getPeripheral( world, pos, facing ); } - - private static void visitBlock( Queue queue, SearchLoc location, int searchID, ICableVisitor visitor ) + + @Override + public boolean canRenderBreaking() { - if( location.distanceTravelled >= 256 ) - { - return; - } - - TileEntity tile = location.world.getTileEntity( location.pos ); - if( tile != null && tile instanceof TileCable ) - { - TileCable modem = (TileCable)tile; - if( !modem.m_destroyed && modem.m_lastSearchID != searchID ) - { - modem.m_lastSearchID = searchID; - visitor.visit( modem, location.distanceTravelled + 1 ); - - enqueue( queue, location.world, location.pos.up(), location.distanceTravelled + 1 ); - enqueue( queue, location.world, location.pos.down(), location.distanceTravelled + 1 ); - enqueue( queue, location.world, location.pos.south(), location.distanceTravelled + 1 ); - enqueue( queue, location.world, location.pos.north(), location.distanceTravelled + 1 ); - enqueue( queue, location.world, location.pos.east(), location.distanceTravelled + 1 ); - enqueue( queue, location.world, location.pos.west(), location.distanceTravelled + 1 ); - } - } + return true; } - private void searchNetwork( ICableVisitor visitor ) + // IWiredElement tile + + @Nullable + @Override + public IWiredElement getWiredElement( @Nonnull EnumFacing side ) { - int searchID = ++s_nextUniqueSearchID; - Queue queue = new LinkedList<>(); - enqueue( queue, getWorld(), getPos(), 1 ); - - //int visited = 0; - while( queue.peek() != null ) - { - SearchLoc loc = queue.remove(); - visitBlock( queue, loc, searchID, visitor ); - //visited++; - } - //System.out.println( "Visited "+visited+" common" ); + return BlockCable.canConnectIn( getBlockState(), side ) ? m_cable : null; } + // IPeripheralTile + @Override - public boolean canRenderBreaking() + public IPeripheral getPeripheral( EnumFacing side ) { - return true; + if( getPeripheralType() != PeripheralType.Cable ) + { + return super.getPeripheral( side ); + } + return null; } } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/modem/WiredModemElement.java b/src/main/java/dan200/computercraft/shared/peripheral/modem/WiredModemElement.java new file mode 100644 index 0000000000..682e8b1cf5 --- /dev/null +++ b/src/main/java/dan200/computercraft/shared/peripheral/modem/WiredModemElement.java @@ -0,0 +1,59 @@ +package dan200.computercraft.shared.peripheral.modem; + +import dan200.computercraft.api.network.wired.IWiredNetworkChange; +import dan200.computercraft.api.network.wired.IWiredElement; +import dan200.computercraft.api.network.wired.IWiredNode; +import dan200.computercraft.api.peripheral.IPeripheral; +import dan200.computercraft.shared.wired.WiredNode; + +import javax.annotation.Nonnull; +import java.util.HashMap; +import java.util.Map; + +public abstract class WiredModemElement implements IWiredElement +{ + private final IWiredNode node = new WiredNode( this ); + private final Map remotePeripherals = new HashMap<>(); + + @Nonnull + @Override + public IWiredNode getNode() + { + return node; + } + + @Nonnull + @Override + public String getSenderID() + { + return "modem"; + } + + @Override + public void networkChanged( @Nonnull IWiredNetworkChange change ) + { + synchronized( remotePeripherals ) + { + remotePeripherals.keySet().removeAll( change.peripheralsRemoved().keySet() ); + for( String name : change.peripheralsRemoved().keySet() ) + { + detachPeripheral( name ); + } + + for( Map.Entry peripheral : change.peripheralsAdded().entrySet() ) + { + attachPeripheral( peripheral.getKey(), peripheral.getValue() ); + } + remotePeripherals.putAll( change.peripheralsAdded() ); + } + } + + public Map getRemotePeripherals() + { + return remotePeripherals; + } + + protected abstract void attachPeripheral( String name, IPeripheral peripheral ); + + protected abstract void detachPeripheral( String name ); +} diff --git a/src/main/java/dan200/computercraft/shared/peripheral/modem/WiredModemPeripheral.java b/src/main/java/dan200/computercraft/shared/peripheral/modem/WiredModemPeripheral.java new file mode 100644 index 0000000000..f139dfc4fc --- /dev/null +++ b/src/main/java/dan200/computercraft/shared/peripheral/modem/WiredModemPeripheral.java @@ -0,0 +1,410 @@ +package dan200.computercraft.shared.peripheral.modem; + +import com.google.common.collect.ImmutableMap; +import dan200.computercraft.api.filesystem.IMount; +import dan200.computercraft.api.filesystem.IWritableMount; +import dan200.computercraft.api.lua.ILuaContext; +import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.api.network.IPacketNetwork; +import dan200.computercraft.api.network.wired.IWiredNode; +import dan200.computercraft.api.network.wired.IWiredSender; +import dan200.computercraft.api.peripheral.IComputerAccess; +import dan200.computercraft.api.peripheral.IPeripheral; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.HashMap; +import java.util.Map; + +import static dan200.computercraft.core.apis.ArgumentHelper.getString; + +public class WiredModemPeripheral extends ModemPeripheral implements IWiredSender +{ + private final WiredModemElement modem; + + private final Map peripheralWrappers = new HashMap<>(); + + public WiredModemPeripheral( WiredModemElement modem ) + { + this.modem = modem; + } + + //region IPacketSender implementation + @Override + public boolean isInterdimensional() + { + return false; + } + + @Override + public double getRange() + { + return 256.0; + } + + @Override + protected IPacketNetwork getNetwork() + { + return modem.getNode(); + } + + @Nonnull + @Override + public World getWorld() + { + return modem.getWorld(); + } + + @Nonnull + @Override + public Vec3d getPosition() + { + return modem.getPosition(); + } + //endregion + + //region IPeripheral + @Nonnull + @Override + public String[] getMethodNames() + { + String[] methods = super.getMethodNames(); + String[] newMethods = new String[methods.length + 5]; + System.arraycopy( methods, 0, newMethods, 0, methods.length ); + newMethods[methods.length] = "getNamesRemote"; + newMethods[methods.length + 1] = "isPresentRemote"; + newMethods[methods.length + 2] = "getTypeRemote"; + newMethods[methods.length + 3] = "getMethodsRemote"; + newMethods[methods.length + 4] = "callRemote"; + return newMethods; + } + + @Override + public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException + { + String[] methods = super.getMethodNames(); + switch( method - methods.length ) + { + case 0: + { + // getNamesRemote + synchronized( peripheralWrappers ) + { + int idx = 1; + Map table = new HashMap<>(); + for( String name : peripheralWrappers.keySet() ) + { + table.put( idx++, name ); + } + return new Object[]{ table }; + } + } + case 1: + { + // isPresentRemote + String type = getTypeRemote( getString( arguments, 0 ) ); + return new Object[]{ type != null }; + } + case 2: + { + // getTypeRemote + String type = getTypeRemote( getString( arguments, 0 ) ); + if( type != null ) + { + return new Object[]{ type }; + } + return null; + } + case 3: + { + // getMethodsRemote + String[] methodNames = getMethodNamesRemote( getString( arguments, 0 ) ); + if( methodNames != null ) + { + Map table = new HashMap<>(); + for( int i = 0; i < methodNames.length; ++i ) + { + table.put( i + 1, methodNames[i] ); + } + return new Object[]{ table }; + } + return null; + } + case 4: + { + // callRemote + String remoteName = getString( arguments, 0 ); + String methodName = getString( arguments, 1 ); + Object[] methodArgs = new Object[arguments.length - 2]; + System.arraycopy( arguments, 2, methodArgs, 0, arguments.length - 2 ); + return callMethodRemote( remoteName, context, methodName, methodArgs ); + } + default: + { + // The regular modem methods + return super.callMethod( computer, context, method, arguments ); + } + } + } + + @Override + public void attach( @Nonnull IComputerAccess computer ) + { + super.attach( computer ); + synchronized( modem.getRemotePeripherals() ) + { + synchronized( peripheralWrappers ) + { + for( Map.Entry entry : modem.getRemotePeripherals().entrySet() ) + { + attachPeripheralImpl( entry.getKey(), entry.getValue() ); + } + } + } + } + + @Override + public synchronized void detach( @Nonnull IComputerAccess computer ) + { + synchronized( peripheralWrappers ) + { + for( RemotePeripheralWrapper wrapper : peripheralWrappers.values() ) + { + wrapper.detach(); + } + peripheralWrappers.clear(); + } + super.detach( computer ); + } + + @Override + public boolean equals( IPeripheral other ) + { + if( other instanceof WiredModemPeripheral ) + { + WiredModemPeripheral otherModem = (WiredModemPeripheral) other; + return otherModem.modem == modem; + } + return false; + } + //endregion + + @Nonnull + @Override + public IWiredNode getNode() + { + return modem.getNode(); + } + + public void attachPeripheral( String name, IPeripheral peripheral ) + { + if( getComputer() == null ) return; + + synchronized( peripheralWrappers ) + { + attachPeripheralImpl( name, peripheral ); + } + } + + public void detachPeripheral( String name ) + { + synchronized( peripheralWrappers ) + { + RemotePeripheralWrapper wrapper = peripheralWrappers.get( name ); + if( wrapper != null ) + { + peripheralWrappers.remove( name ); + wrapper.detach(); + } + } + } + + private void attachPeripheralImpl( String periphName, IPeripheral peripheral ) + { + if( !peripheralWrappers.containsKey( periphName ) ) + { + RemotePeripheralWrapper wrapper = new RemotePeripheralWrapper( modem, peripheral, getComputer(), periphName ); + peripheralWrappers.put( periphName, wrapper ); + wrapper.attach(); + } + } + + private String getTypeRemote( String remoteName ) + { + synchronized( peripheralWrappers ) + { + RemotePeripheralWrapper wrapper = peripheralWrappers.get( remoteName ); + if( wrapper != null ) + { + return wrapper.getType(); + } + } + return null; + } + + private String[] getMethodNamesRemote( String remoteName ) + { + synchronized( peripheralWrappers ) + { + RemotePeripheralWrapper wrapper = peripheralWrappers.get( remoteName ); + if( wrapper != null ) + { + return wrapper.getMethodNames(); + } + } + return null; + } + + private Object[] callMethodRemote( String remoteName, ILuaContext context, String method, Object[] arguments ) throws LuaException, InterruptedException + { + RemotePeripheralWrapper wrapper; + synchronized( peripheralWrappers ) + { + wrapper = peripheralWrappers.get( remoteName ); + } + if( wrapper != null ) + { + return wrapper.callMethod( context, method, arguments ); + } + throw new LuaException( "No peripheral: " + remoteName ); + } + + private static class RemotePeripheralWrapper implements IComputerAccess + { + private final WiredModemElement m_element; + private final IPeripheral m_peripheral; + private final IComputerAccess m_computer; + private final String m_name; + + private final String m_type; + private final String[] m_methods; + private final Map m_methodMap; + + public RemotePeripheralWrapper( WiredModemElement element, IPeripheral peripheral, IComputerAccess computer, String name ) + { + m_element = element; + m_peripheral = peripheral; + m_computer = computer; + m_name = name; + + m_type = peripheral.getType(); + m_methods = peripheral.getMethodNames(); + assert (m_type != null); + assert (m_methods != null); + + m_methodMap = new HashMap<>(); + for( int i = 0; i < m_methods.length; ++i ) + { + if( m_methods[i] != null ) + { + m_methodMap.put( m_methods[i], i ); + } + } + } + + public void attach() + { + m_peripheral.attach( this ); + m_computer.queueEvent( "peripheral", new Object[]{ getAttachmentName() } ); + } + + public void detach() + { + m_peripheral.detach( this ); + m_computer.queueEvent( "peripheral_detach", new Object[]{ getAttachmentName() } ); + } + + public String getType() + { + return m_type; + } + + public String[] getMethodNames() + { + return m_methods; + } + + public Object[] callMethod( ILuaContext context, String methodName, Object[] arguments ) throws LuaException, InterruptedException + { + if( m_methodMap.containsKey( methodName ) ) + { + int method = m_methodMap.get( methodName ); + return m_peripheral.callMethod( this, context, method, arguments ); + } + throw new LuaException( "No such method " + methodName ); + } + + // IComputerAccess implementation + + @Override + public String mount( @Nonnull String desiredLocation, @Nonnull IMount mount ) + { + return m_computer.mount( desiredLocation, mount, m_name ); + } + + @Override + public String mount( @Nonnull String desiredLocation, @Nonnull IMount mount, @Nonnull String driveName ) + { + return m_computer.mount( desiredLocation, mount, driveName ); + } + + @Override + public String mountWritable( @Nonnull String desiredLocation, @Nonnull IWritableMount mount ) + { + return m_computer.mountWritable( desiredLocation, mount, m_name ); + } + + @Override + public String mountWritable( @Nonnull String desiredLocation, @Nonnull IWritableMount mount, @Nonnull String driveName ) + { + return m_computer.mountWritable( desiredLocation, mount, driveName ); + } + + @Override + public void unmount( String location ) + { + m_computer.unmount( location ); + } + + @Override + public int getID() + { + return m_computer.getID(); + } + + @Override + public void queueEvent( @Nonnull String event, Object[] arguments ) + { + m_computer.queueEvent( event, arguments ); + } + + @Nonnull + @Override + public String getAttachmentName() + { + return m_name; + } + + @Nonnull + @Override + public Map getAvailablePeripherals() + { + synchronized( m_element.getRemotePeripherals() ) + { + return ImmutableMap.copyOf( m_element.getRemotePeripherals() ); + } + } + + @Nullable + @Override + public IPeripheral getAvailablePeripheral( @Nonnull String name ) + { + synchronized( m_element.getRemotePeripherals() ) + { + return m_element.getRemotePeripherals().get( name ); + } + } + } +}