CC-Tweaked/src/main/java/dan200/computercraft/shared/peripheral/modem/ModemPeripheral.java

251 lines
6.9 KiB
Java

/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.peripheral.modem;
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.IPacketSender;
import dan200.computercraft.api.network.Packet;
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 java.util.HashSet;
import java.util.Set;
import static dan200.computercraft.core.apis.ArgumentHelper.getInt;
public abstract class ModemPeripheral implements IPeripheral, IPacketSender, IPacketReceiver
{
private IPacketNetwork m_network;
private final Set<IComputerAccess> m_computers = new HashSet<>( 1 );
private final ModemState m_state;
protected ModemPeripheral( ModemState state )
{
m_state = state;
}
public ModemState getModemState()
{
return m_state;
}
@Deprecated
public boolean pollChanged()
{
// Only for backwards compatibility
return m_state.pollChanged();
}
@Deprecated
public boolean isActive()
{
// Only for backwards compatibility
return m_state.isOpen();
}
private synchronized void setNetwork( IPacketNetwork network )
{
if( m_network == network ) return;
// Leave old network
if( m_network != null ) m_network.removeReceiver( this );
// Set new network
m_network = network;
// Join new network
if( m_network != null ) m_network.addReceiver( this );
}
protected void switchNetwork()
{
setNetwork( getNetwork() );
}
public void destroy()
{
setNetwork( null );
}
@Override
public void receiveSameDimension( @Nonnull Packet packet, double distance )
{
if( packet.getSender() == this || !m_state.isOpen( packet.getChannel() ) ) return;
synchronized( m_computers )
{
for( IComputerAccess computer : m_computers )
{
computer.queueEvent( "modem_message", new Object[] {
computer.getAttachmentName(), packet.getChannel(), packet.getReplyChannel(), packet.getPayload(), distance,
} );
}
}
}
@Override
public void receiveDifferentDimension( @Nonnull Packet packet )
{
if( packet.getSender() == this || !m_state.isOpen( packet.getChannel() ) ) return;
synchronized( m_computers )
{
for( IComputerAccess computer : m_computers )
{
computer.queueEvent( "modem_message", new Object[] {
computer.getAttachmentName(), packet.getChannel(), packet.getReplyChannel(), packet.getPayload(),
} );
}
}
}
protected abstract IPacketNetwork getNetwork();
// IPeripheral implementation
@Nonnull
@Override
public String getType()
{
return "modem";
}
@Nonnull
@Override
public String[] getMethodNames()
{
return new String[] {
"open",
"isOpen",
"close",
"closeAll",
"transmit",
"isWireless",
};
}
private static int parseChannel( Object[] arguments, int index ) throws LuaException
{
int channel = getInt( arguments, index );
if( channel < 0 || channel > 65535 )
{
throw new LuaException( "Expected number in range 0-65535" );
}
return channel;
}
@Override
public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException
{
switch( method )
{
case 0:
{
// open
int channel = parseChannel( arguments, 0 );
m_state.open( channel );
return null;
}
case 1:
{
// isOpen
int channel = parseChannel( arguments, 0 );
return new Object[] { m_state.isOpen( channel ) };
}
case 2:
{
// close
int channel = parseChannel( arguments, 0 );
m_state.close( channel );
return null;
}
case 3: // closeAll
m_state.closeAll();
return null;
case 4:
{
// transmit
int channel = parseChannel( arguments, 0 );
int replyChannel = parseChannel( arguments, 1 );
Object payload = arguments.length > 2 ? arguments[2] : null;
World world = getWorld();
Vec3d position = getPosition();
IPacketNetwork network = m_network;
if( world != null && position != null && network != null )
{
Packet packet = new Packet( channel, replyChannel, payload, this );
if( isInterdimensional() )
{
network.transmitInterdimensional( packet );
}
else
{
network.transmitSameDimension( packet, getRange() );
}
}
return null;
}
case 5:
{
// isWireless
IPacketNetwork network = m_network;
return new Object[] { network != null && network.isWireless() };
}
default:
return null;
}
}
@Override
public synchronized void attach( @Nonnull IComputerAccess computer )
{
synchronized( m_computers )
{
m_computers.add( computer );
}
setNetwork( getNetwork() );
}
@Override
public synchronized void detach( @Nonnull IComputerAccess computer )
{
boolean empty;
synchronized( m_computers )
{
m_computers.remove( computer );
empty = m_computers.isEmpty();
}
if( empty ) setNetwork( null );
}
@Nonnull
@Override
public String getSenderID()
{
synchronized( m_computers )
{
if( m_computers.size() != 1 )
{
return "unknown";
}
else
{
IComputerAccess computer = m_computers.iterator().next();
return computer.getID() + "_" + computer.getAttachmentName();
}
}
}
}