407 lines
11 KiB
Java
407 lines
11 KiB
Java
/**
|
|
* This file is part of ComputerCraft - http://www.computercraft.info
|
|
* Copyright Daniel Ratcliffe, 2011-2016. 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.peripheral.IComputerAccess;
|
|
import dan200.computercraft.api.peripheral.IPeripheral;
|
|
import net.minecraft.util.math.Vec3d;
|
|
import net.minecraft.world.World;
|
|
|
|
import java.util.HashMap;
|
|
import java.util.Iterator;
|
|
import java.util.Map;
|
|
|
|
public abstract class ModemPeripheral
|
|
implements IPeripheral
|
|
{
|
|
private static class SingleChannelReceiver implements IReceiver
|
|
{
|
|
private ModemPeripheral m_owner;
|
|
private int m_channel;
|
|
|
|
public SingleChannelReceiver( ModemPeripheral owner, int channel )
|
|
{
|
|
m_owner = owner;
|
|
m_channel = channel;
|
|
}
|
|
|
|
// IReceiver implementation
|
|
|
|
@Override
|
|
public int getChannel()
|
|
{
|
|
return m_channel;
|
|
}
|
|
|
|
@Override
|
|
public World getWorld()
|
|
{
|
|
return m_owner.getWorld();
|
|
}
|
|
|
|
@Override
|
|
public Vec3d getWorldPosition()
|
|
{
|
|
return m_owner.getWorldPosition();
|
|
}
|
|
|
|
@Override
|
|
public boolean isInterdimensional()
|
|
{
|
|
return m_owner.isInterdimensional();
|
|
}
|
|
|
|
@Override
|
|
public double getReceiveRange()
|
|
{
|
|
return m_owner.getReceiveRange();
|
|
}
|
|
|
|
@Override
|
|
public void receiveSameDimension( int replyChannel, Object payload, double distance, Object senderObject )
|
|
{
|
|
if( senderObject != m_owner )
|
|
{
|
|
m_owner.receiveSameDimension( m_channel, replyChannel, payload, distance );
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void receiveDifferentDimension( int replyChannel, Object payload, Object senderObject )
|
|
{
|
|
if( senderObject != m_owner )
|
|
{
|
|
m_owner.receiveDifferentDimension( m_channel, replyChannel, payload );
|
|
}
|
|
}
|
|
}
|
|
|
|
private INetwork m_network;
|
|
private IComputerAccess m_computer;
|
|
private Map<Integer, IReceiver> m_channels;
|
|
|
|
private boolean m_open;
|
|
private boolean m_changed;
|
|
|
|
public ModemPeripheral()
|
|
{
|
|
m_network = null;
|
|
m_computer = null;
|
|
|
|
m_channels = new HashMap<Integer, IReceiver>();
|
|
m_open = false;
|
|
m_changed = true;
|
|
}
|
|
|
|
private synchronized void setNetwork( INetwork network )
|
|
{
|
|
if( m_network != network )
|
|
{
|
|
// Leave old network
|
|
if( m_network != null )
|
|
{
|
|
Iterator<IReceiver> it = m_channels.values().iterator();
|
|
while( it.hasNext() )
|
|
{
|
|
m_network.removeReceiver( it.next() );
|
|
}
|
|
}
|
|
|
|
// Set new network
|
|
m_network = network;
|
|
|
|
// Join new network
|
|
if( m_network != null )
|
|
{
|
|
Iterator<IReceiver> it = m_channels.values().iterator();
|
|
while( it.hasNext() )
|
|
{
|
|
m_network.addReceiver( it.next() );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
protected void switchNetwork()
|
|
{
|
|
setNetwork( getNetwork() );
|
|
}
|
|
|
|
protected abstract World getWorld();
|
|
|
|
protected abstract Vec3d getPosition();
|
|
|
|
public synchronized void destroy()
|
|
{
|
|
setNetwork( null );
|
|
m_channels.clear();
|
|
m_open = false;
|
|
}
|
|
|
|
public synchronized boolean pollChanged()
|
|
{
|
|
if( m_changed )
|
|
{
|
|
m_changed = false;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
protected abstract double getTransmitRange();
|
|
|
|
protected abstract boolean isInterdimensional();
|
|
|
|
public synchronized boolean isActive()
|
|
{
|
|
return (m_computer != null) && m_open;
|
|
}
|
|
|
|
public synchronized Vec3d getWorldPosition()
|
|
{
|
|
return getPosition();
|
|
}
|
|
|
|
public synchronized double getReceiveRange()
|
|
{
|
|
return getTransmitRange();
|
|
}
|
|
|
|
public void receiveSameDimension( int channel, int replyChannel, Object payload, double distance )
|
|
{
|
|
synchronized (m_channels)
|
|
{
|
|
if( m_computer != null && m_channels.containsKey( channel ) )
|
|
{
|
|
m_computer.queueEvent( "modem_message", new Object[] {
|
|
m_computer.getAttachmentName(), channel, replyChannel, payload, distance
|
|
} );
|
|
}
|
|
}
|
|
}
|
|
|
|
public void receiveDifferentDimension( int channel, int replyChannel, Object payload )
|
|
{
|
|
synchronized (m_channels)
|
|
{
|
|
if( m_computer != null && m_channels.containsKey( channel ) )
|
|
{
|
|
m_computer.queueEvent( "modem_message", new Object[] {
|
|
m_computer.getAttachmentName(), channel, replyChannel, payload
|
|
} );
|
|
}
|
|
}
|
|
}
|
|
|
|
protected abstract INetwork getNetwork();
|
|
|
|
// IPeripheral implementation
|
|
|
|
@Override
|
|
public String getType()
|
|
{
|
|
return "modem";
|
|
}
|
|
|
|
@Override
|
|
public String[] getMethodNames()
|
|
{
|
|
return new String[] {
|
|
"open",
|
|
"isOpen",
|
|
"close",
|
|
"closeAll",
|
|
"transmit",
|
|
"isWireless",
|
|
};
|
|
}
|
|
|
|
private static int parseChannel( Object[] arguments, int index ) throws LuaException
|
|
{
|
|
if( arguments.length <= index || !(arguments[index] instanceof Double) )
|
|
{
|
|
throw new LuaException( "Expected number" );
|
|
}
|
|
int channel = (int)((Double)arguments[index]).doubleValue();
|
|
if( channel < 0 || channel > 65535 )
|
|
{
|
|
throw new LuaException( "Expected number in range 0-65535" );
|
|
}
|
|
return channel;
|
|
}
|
|
|
|
@Override
|
|
public Object[] callMethod( IComputerAccess computer, ILuaContext context, int method, Object[] arguments ) throws LuaException, InterruptedException
|
|
{
|
|
switch( method )
|
|
{
|
|
case 0:
|
|
{
|
|
// open
|
|
int channel = parseChannel( arguments, 0 );
|
|
synchronized( this )
|
|
{
|
|
if( !m_channels.containsKey( channel ) )
|
|
{
|
|
if( m_channels.size() >= 128 )
|
|
{
|
|
throw new LuaException( "Too many open channels" );
|
|
}
|
|
|
|
IReceiver receiver = new SingleChannelReceiver( this, channel );
|
|
m_channels.put( channel, receiver );
|
|
if( m_network != null )
|
|
{
|
|
m_network.addReceiver( receiver );
|
|
}
|
|
if( !m_open )
|
|
{
|
|
m_open = true;
|
|
m_changed = true;
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
case 1:
|
|
{
|
|
// isOpen
|
|
int channel = parseChannel( arguments, 0 );
|
|
synchronized( this )
|
|
{
|
|
boolean open = m_channels.containsKey( channel );
|
|
return new Object[] { open };
|
|
}
|
|
}
|
|
case 2:
|
|
{
|
|
// close
|
|
int channel = parseChannel( arguments, 0 );
|
|
synchronized( this )
|
|
{
|
|
if( m_channels.containsKey( channel ) )
|
|
{
|
|
IReceiver receiver = m_channels.get( channel );
|
|
if( m_network != null )
|
|
{
|
|
m_network.removeReceiver( receiver );
|
|
}
|
|
m_channels.remove( channel );
|
|
|
|
if( m_channels.size() == 0 )
|
|
{
|
|
m_open = false;
|
|
m_changed = true;
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
case 3:
|
|
{
|
|
// closeAll
|
|
synchronized( this )
|
|
{
|
|
if( m_channels.size() > 0 )
|
|
{
|
|
if( m_network != null )
|
|
{
|
|
Iterator<IReceiver> it = m_channels.values().iterator();
|
|
while( it.hasNext() )
|
|
{
|
|
m_network.removeReceiver( it.next() );
|
|
}
|
|
}
|
|
m_channels.clear();
|
|
|
|
if( m_open )
|
|
{
|
|
m_open = false;
|
|
m_changed = true;
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
case 4:
|
|
{
|
|
// transmit
|
|
int channel = parseChannel( arguments, 0 );
|
|
int replyChannel = parseChannel( arguments, 1 );
|
|
Object payload = (arguments.length >= 3) ? arguments[2] : null;
|
|
synchronized( this )
|
|
{
|
|
World world = getWorld();
|
|
Vec3d position = getPosition();
|
|
if( world != null && position != null && m_network != null)
|
|
{
|
|
m_network.transmit( channel, replyChannel, payload, world, position, getTransmitRange(), isInterdimensional(), this );
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
case 5:
|
|
{
|
|
// isWireless
|
|
synchronized( this )
|
|
{
|
|
if( m_network != null )
|
|
{
|
|
return new Object[] { m_network.isWireless() };
|
|
}
|
|
}
|
|
return new Object[] { false };
|
|
}
|
|
default:
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public synchronized void attach( IComputerAccess computer )
|
|
{
|
|
m_computer = computer;
|
|
setNetwork( getNetwork() );
|
|
m_open = !m_channels.isEmpty();
|
|
}
|
|
|
|
@Override
|
|
public synchronized void detach( IComputerAccess computer )
|
|
{
|
|
if( m_network != null )
|
|
{
|
|
Iterator<IReceiver> it = m_channels.values().iterator();
|
|
while( it.hasNext() )
|
|
{
|
|
m_network.removeReceiver( it.next() );
|
|
}
|
|
m_channels.clear();
|
|
m_network = null;
|
|
}
|
|
|
|
m_computer = null;
|
|
|
|
if( m_open )
|
|
{
|
|
m_open = false;
|
|
m_changed = true;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public abstract boolean equals( IPeripheral other );
|
|
|
|
public IComputerAccess getComputer()
|
|
{
|
|
return m_computer;
|
|
}
|
|
}
|