package nbtool.io;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.util.Timer;
import java.util.TimerTask;
import data.GameControlData;
import data.SPLStandardMessage;
import messages.TeamPacket;
import messages.WorldModel;
import nbtool.data.NBitesTeamBroadcast;
import nbtool.io.CommonIO.IOFirstResponder;
import nbtool.io.CommonIO.IOInstance;
import nbtool.io.CommonIO.IOState;
import nbtool.util.Debug;
import nbtool.util.Robots;
import nbtool.util.ToolSettings;
public class BroadcastIO {
public static int teamPort(int teamNum) {return 10000 + teamNum;}
public static final String BROADCAST_ADDRESS = "10.0.255.255";
public static final int NBITES_TEAM_PORT = teamPort(ToolSettings.NBITES_TEAM_NUM);
public static final int GAME_CONTROL_PORT = GameControlData.GAMECONTROLLER_GAMEDATA_PORT;
public static final int GAME_CONTROL_RECV_PORT = GameControlData.GAMECONTROLLER_RETURNDATA_PORT;
public static TeamBroadcastInstance createTBI(TeamBroadcastListener list, int prt) {
TeamBroadcastInstance ret = new TeamBroadcastInstance(prt);
ret.ifr = list;
Thread t = new Thread(ret, String.format("nbtool-%s", ret.name()));
t.setDaemon(true);
t.start();
return ret;
}
public static class TeamBroadcastInstance extends IOInstance {
public TeamBroadcastInstance(int port) {
this.port = port;
}
private DatagramSocket datagramSocket;
@Override
public void run() {
try {
datagramSocket = new DatagramSocket(null);
datagramSocket.setReuseAddress(true);
datagramSocket.setSoTimeout(500);
datagramSocket.bind(new InetSocketAddress(port));
} catch (SocketException e) {
Debug.error("TeamBroadcastListener could not bind to port %d!", port);
this.state = IOState.FINISHED;
return;
}
Debug.print("%s bound and running.", name());
synchronized(this) {
if (this.state != IOState.STARTING)
return;
this.state = IOState.RUNNING;
}
while (state() == IOState.RUNNING) {
final ByteBuffer buffer = ByteBuffer.wrap(new byte[SPLStandardMessage.SIZE]);
final SPLStandardMessage data = new SPLStandardMessage();
final DatagramPacket packet = new DatagramPacket(buffer.array(), buffer.array().length);
try {
datagramSocket.receive(packet);
buffer.rewind();
String address = packet.getAddress().getHostAddress();
String name = Robots.IP_TO_ROBOT.get(address).name;
data.fromByteArray(buffer);
TeamPacket tp = TeamPacket.parseFrom(data.data);
WorldModel wm = tp.getPayload();
NBitesTeamBroadcast tb = new NBitesTeamBroadcast(name, address,
data, tp, wm);
if (this.ifr instanceof TeamBroadcastListener) {
((TeamBroadcastListener) this.ifr).acceptTeamBroadcast(tb);
} else {
ifr.ioReceived(this, 0, tb.toLog());
}
} catch (SocketTimeoutException e) { // ignore, because we set a timeout
} catch (IOException e) {
Debug.error("TeamBroadcastListener got exception: %s", e.getMessage());
e.printStackTrace();
}
}
}
@Override
public String name() {
return "TeamBroadcastListener(port=" + port + ")";
}
}
public static interface TeamBroadcastListener extends IOFirstResponder {
public void acceptTeamBroadcast(NBitesTeamBroadcast tb);
}
public static abstract class BroadcastDataProvider {
/* somewhat arbitrary, based on underlying datagram protocol */
public static final int MAX_BROADCAST_SIZE = 1200;
public abstract byte[] provideBroadcast();
public abstract String name();
}
public static abstract class SPLMessageProvider extends BroadcastDataProvider {
@Override
public byte[] provideBroadcast() {
return provideMessage().toByteArray();
}
public abstract SPLStandardMessage provideMessage();
}
public static class Broadcaster {
//may NOT be set to null.
public volatile BroadcastDataProvider provider;
public volatile int interim;
public volatile boolean running;
public Broadcaster(String address, int port) throws SocketException, UnknownHostException {
this.provider = null;
this.interim = 1000;
this.running = false;
this.socket = new DatagramSocket();
this.destination = InetAddress.getByName(address);
this.destPort = port;
this.timer = new Timer("Broadcaster-timer", true);
this.timer.schedule(new BroadcastTask(), 1000);
}
private DatagramSocket socket;
private InetAddress destination;
private int destPort;
private Timer timer;
private class BroadcastTask extends TimerTask {
@Override
public void run() {
if (running) {
byte[] data = provider.provideBroadcast();
if (data != null
&& data.length < BroadcastDataProvider.MAX_BROADCAST_SIZE) {
try {
Debug.info("BroadcastTask sending [%d] bytes from [%s].", data.length,
provider.name());
DatagramPacket packet = new DatagramPacket(data, data.length,
destination, destPort);
socket.send(packet);
} catch (Exception e) {
e.printStackTrace();
Debug.error("Broadcaster exiting...");
return;
}
timer.schedule(new BroadcastTask(), interim);
return;
}
}
timer.schedule(new BroadcastTask(), 1000);
}
}
}
}