From 5c7828dd799656ae324c9aa7f1827c2d9fa74a7e Mon Sep 17 00:00:00 2001 From: SquidDev Date: Wed, 21 Feb 2018 15:35:38 +0000 Subject: [PATCH] Convert TileCable to use the wired network API There are several important things to note here: - The network element is associated with the cable, whilst the peripheral (and so packet sender/receiver) is associated with the modem. This allows us to have the main element be in the centre of the cable block, whilst the modem is in the centre of the adjacent computer. - Cables will connect to any adjacent network element, not just other cables. - Rednet messages are now sent on the computer thread, rather than the cable tick. --- .../client/render/RenderOverlayCable.java | 4 +- .../shared/peripheral/common/BlockCable.java | 57 +- .../shared/peripheral/modem/TileCable.java | 813 ++++-------------- .../peripheral/modem/WiredModemElement.java | 59 ++ .../modem/WiredModemPeripheral.java | 410 +++++++++ 5 files changed, 655 insertions(+), 688 deletions(-) create mode 100644 src/main/java/dan200/computercraft/shared/peripheral/modem/WiredModemElement.java create mode 100644 src/main/java/dan200/computercraft/shared/peripheral/modem/WiredModemPeripheral.java diff --git a/src/main/java/dan200/computercraft/client/render/RenderOverlayCable.java b/src/main/java/dan200/computercraft/client/render/RenderOverlayCable.java index 5ee9e430f..138ad3fff 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 class RenderOverlayCable 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 class RenderOverlayCable 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 801f001cb..f0e04a4db 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.common.TileGeneric; 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 class BlockCable extends BlockPeripheralBase 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 class BlockCable extends BlockPeripheralBase } } - 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 @@ public class BlockCable extends BlockPeripheralBase @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 class BlockCable extends BlockPeripheralBase 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 class BlockCable extends BlockPeripheralBase 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 class BlockCable extends BlockPeripheralBase { 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 c7aa49efb..8bad64848 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 @@ package dan200.computercraft.shared.peripheral.modem; 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.BlockCableModemVariant; 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.util.text.TextComponentTranslation; 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; - } + private final TileCable m_entity; - @Override - public boolean isInterdimensional() + private CableElement( TileCable m_entity ) { - return false; - } - - @Override - public double getRange() - { - return 256.0; - } - - @Override - protected IPacketNetwork getNetwork() - { - return m_entity; + this.m_entity = m_entity; } @Nonnull @@ -99,168 +74,81 @@ public class TileCable extends TileModemBase @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() + public Map getPeripherals() { - 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; + IPeripheral peripheral = m_entity.getConnectedPeripheral(); + return peripheral != null + ? Collections.singletonMap( m_entity.getConnectedPeripheralName(), peripheral ) + : Collections.emptyMap(); } @Override - public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException + protected void attachPeripheral( String name, IPeripheral peripheral ) { - String[] methods = super.getMethodNames(); - switch( method - methods.length ) + if( !name.equals( m_entity.getConnectedPeripheralName() ) ) { - 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 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 class TileCable extends TileModemBase 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 class TileCable extends TileModemBase 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 class TileCable extends TileModemBase 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 class TileCable extends TileModemBase 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 class TileCable extends TileModemBase 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 class TileCable extends TileModemBase } @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 @@ public class TileCable extends TileModemBase 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; - } - } - synchronized( m_transmitQueue ) - { - while( m_transmitQueue.peek() != null ) - { - PacketWrapper p = m_transmitQueue.remove(); - if( p != null ) - { - dispatchPacket( p ); - } - } + networkChanged(); + if( m_peripheralAccessAllowed ) m_node.invalidate(); + m_connectionsFormed = true; } } } - // 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 ) - { - if( !m_peripheralWrappersByName.containsKey( periphName ) ) - { - RemotePeripheralWrapper wrapper = new RemotePeripheralWrapper( peripheral, m_modem.getComputer(), periphName ); - m_peripheralWrappersByName.put( periphName, wrapper ); - wrapper.attach(); - } - } - - private void detachPeripheral( String periphName ) - { - if( m_peripheralWrappersByName.containsKey( periphName ) ) - { - RemotePeripheralWrapper wrapper = m_peripheralWrappersByName.get( periphName ); - m_peripheralWrappersByName.remove( periphName ); - wrapper.detach(); - } - } - - 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 ) - { - return wrapper.callMethod( context, method, arguments ); - } - throw new LuaException( "No peripheral: "+remoteName ); - } - public void networkChanged() { - if( !getWorld().isRemote ) + if( getWorld().isRemote ) return; + + if( modemChanged() ) m_node.invalidate(); + + IBlockState state = getBlockState(); + World world = getWorld(); + BlockPos current = getPos(); + for( EnumFacing facing : EnumFacing.VALUES ) { - if( !m_destroyed && getPeripheralType() != PeripheralType.WiredModem) + if( !world.isBlockLoaded( pos ) ) continue; + + IWiredElement element = ComputerCraft.getWiredElementAt( world, current.offset( facing ), facing.getOpposite() ); + if( element == null ) continue; + + 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 class TileCable extends TileModemBase 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 void togglePeripheralAccess() { if( !m_peripheralAccessAllowed ) { @@ -986,11 +545,12 @@ public class TileCable extends TileModemBase { 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 class TileCable extends TileModemBase 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,79 +576,19 @@ public class TileCable extends TileModemBase { 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 ) - { - 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 ); - } - } - - private static void visitBlock( Queue queue, SearchLoc location, int searchID, ICableVisitor visitor ) - { - 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 ); - } - } - } - private void searchNetwork( ICableVisitor visitor ) + public static IPeripheral getPeripheral( World world, BlockPos pos, EnumFacing facing ) { - 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" ); + Block block = world.getBlockState( pos ).getBlock(); + if( block == ComputerCraft.Blocks.wiredModemFull || block == ComputerCraft.Blocks.cable ) return null; + + return PeripheralUtil.getPeripheral( world, pos, facing ); } @Override @@ -1096,4 +596,25 @@ public class TileCable extends TileModemBase { return true; } + + // IWiredElement tile + + @Nullable + @Override + public IWiredElement getWiredElement( @Nonnull EnumFacing side ) + { + return BlockCable.canConnectIn( getBlockState(), side ) ? m_cable : null; + } + + // IPeripheralTile + + @Override + public IPeripheral getPeripheral( EnumFacing side ) + { + 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 000000000..682e8b1cf --- /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 000000000..f139dfc4f --- /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 ); + } + } + } +}