1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-10-23 18:07:39 +00:00

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.
This commit is contained in:
SquidDev
2018-02-21 15:35:38 +00:00
parent 74f5093d2a
commit 5c7828dd79
5 changed files with 655 additions and 688 deletions

View File

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

View File

@@ -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;
return state.getValue( BlockCable.Properties.CABLE ) != BlockCableCableVariant.NONE
&& state.getValue( BlockCable.Properties.MODEM ).getFacing() != direction;
}
else if( state.getValue( Properties.MODEM ).getFacing() == dir )
public static boolean doesConnectVisually( IBlockState state, IBlockAccess world, BlockPos pos, EnumFacing direction )
{
return true;
}
else
{
return isCable( world, pos.offset( dir ) );
}
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 )
{

View File

@@ -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;
private final TileCable m_entity;
public Peripheral( TileCable entity )
private CableElement( TileCable m_entity )
{
m_entity = entity;
}
@Override
public boolean isInterdimensional()
{
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<String, IPeripheral> 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<Object,Object> 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<Object,Object> 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 m_entity.callMethodRemote( remoteName, context, methodName, methodArgs );
}
default:
{
// The regular modem methods
return super.callMethod( computer, context, method, arguments );
}
((WiredModemPeripheral) m_entity.m_modem).attachPeripheral( name, peripheral );
}
}
@Override
public void attach( @Nonnull IComputerAccess computer )
protected void detachPeripheral( String name )
{
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 );
((WiredModemPeripheral) m_entity.m_modem).detachPeripheral( name );
}
}
}
}
@Override
public synchronized void detach( @Nonnull IComputerAccess computer )
{
synchronized( m_entity.m_peripheralsByName )
{
for (String periphName : m_entity.m_peripheralsByName.keySet())
{
m_entity.detachPeripheral( periphName );
}
}
super.detach( computer );
}
@Override
public boolean equals( IPeripheral other )
{
if( other instanceof Peripheral )
{
Peripheral otherModem = (Peripheral)other;
return otherModem.m_entity == m_entity;
}
return false;
}
}
private static int s_nextUniqueSearchID = 1;
// Members
private final Set<IPacketReceiver> m_receivers;
private final Queue<PacketWrapper> m_transmitQueue;
private boolean m_peripheralAccessAllowed;
private int m_attachedPeripheralID;
private final Map<String, IPeripheral> m_peripheralsByName;
private Map<String, RemotePeripheralWrapper> 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()
{
@@ -375,7 +277,9 @@ public class TileCable extends TileModemBase
// Drop everything and remove block
((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:
{
@@ -383,7 +287,8 @@ public class TileCable extends TileModemBase
((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;
}
}
@@ -395,7 +300,7 @@ public class TileCable extends TileModemBase
return super.getBounds();
}
public AxisAlignedBB getCableBounds()
private AxisAlignedBB getCableBounds()
{
double xMin = 0.375;
double yMin = 0.375;
@@ -405,27 +310,29 @@ 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;
}
@@ -468,10 +375,11 @@ public class TileCable extends TileModemBase
if( type == PeripheralType.Cable || type == PeripheralType.WiredModemWithCable )
{
bounds.add( BOX_CENTRE );
BlockPos pos = getPos();
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()] );
}
@@ -538,12 +446,6 @@ public class TileCable extends TileModemBase
return nbttagcompound;
}
@Override
protected ModemPeripheral createPeripheral()
{
return new Peripheral( this );
}
@Override
protected void updateAnim()
{
@@ -559,18 +461,6 @@ 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()
{
@@ -578,168 +468,45 @@ public class TileCable extends TileModemBase
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 )
{
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;
@@ -763,215 +530,7 @@ public class TileCable extends TileModemBase
}
// 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<String, Integer> 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<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;
}
}
private void findPeripherals( )
{
final TileCable origin = this;
synchronized( m_peripheralsByName )
{
// Collect the peripherals
final Map<String, IPeripheral> 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<String> 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();
m_node.invalidate();
}
public String getConnectedPeripheralName()
private String getConnectedPeripheralName()
{
IPeripheral periph = getConnectedPeripheral();
if( periph != null )
@@ -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
public static IPeripheral getPeripheral( World world, BlockPos pos, EnumFacing facing )
{
Block block = world.getBlockState( pos ).getBlock();
if( block == ComputerCraft.Blocks.wiredModemFull || block == ComputerCraft.Blocks.cable ) return null;
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<SearchLoc> 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<SearchLoc> 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 )
{
int searchID = ++s_nextUniqueSearchID;
Queue<SearchLoc> 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 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;
}
}

View File

@@ -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<String, IPeripheral> 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<String, IPeripheral> peripheral : change.peripheralsAdded().entrySet() )
{
attachPeripheral( peripheral.getKey(), peripheral.getValue() );
}
remotePeripherals.putAll( change.peripheralsAdded() );
}
}
public Map<String, IPeripheral> getRemotePeripherals()
{
return remotePeripherals;
}
protected abstract void attachPeripheral( String name, IPeripheral peripheral );
protected abstract void detachPeripheral( String name );
}

View File

@@ -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<String, RemotePeripheralWrapper> 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<Object, Object> 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<Object, Object> 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<String, IPeripheral> 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<String, Integer> 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<String, IPeripheral> 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 );
}
}
}
}