package teamcomm.net; import common.Log; import data.SPLStandardMessage; import java.io.IOException; import java.net.DatagramPacket; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.MulticastSocket; import java.net.NetworkInterface; import java.net.SocketException; import java.net.SocketTimeoutException; import java.nio.ByteBuffer; import java.util.Enumeration; import java.util.concurrent.LinkedBlockingQueue; import javax.swing.JOptionPane; import teamcomm.PluginLoader; import teamcomm.data.GameState; import teamcomm.data.AdvancedMessage; import teamcomm.net.logging.LogReplayer; import teamcomm.net.logging.Logger; /** * Singleton class for the thread which handles messages from the robots. It * spawns one thread for listening on each team port up to team number 100 and * processes the messages received by these threads. * * @author Felix Thielke */ public class SPLStandardMessageReceiver extends Thread { private class ReceiverThread extends Thread { private final MulticastSocket socket; private final int team; public ReceiverThread(final int team) throws IOException { setName("SPLStandardMessageReceiver_team" + team); this.team = team; // Bind socket to team port socket = new MulticastSocket(null); socket.setReuseAddress(true); socket.bind(new InetSocketAddress("0.0.0.0", getTeamport(team))); try { // Join multicast group on all network interfaces (for compatibility with SimRobot) socket.joinGroup(InetAddress.getByName("239.0.0.1")); final byte[] localaddr = InetAddress.getLocalHost().getAddress(); localaddr[0] = (byte) 239; socket.joinGroup(InetAddress.getByAddress(localaddr)); final Enumeration<NetworkInterface> nis = NetworkInterface.getNetworkInterfaces(); while (nis.hasMoreElements()) { final NetworkInterface ni = nis.nextElement(); final Enumeration<InetAddress> addrs = ni.getInetAddresses(); while (addrs.hasMoreElements()) { final byte[] addr = addrs.nextElement().getAddress(); addr[0] = (byte) 239; socket.joinGroup(InetAddress.getByAddress(addr)); } } } catch (SocketException ex) { // Ignore, because this is only for testing and does not work everywhere } } @Override public void run() { byte[] buffer = new byte[SPLStandardMessage.SIZE]; while (!isInterrupted()) { try { final DatagramPacket packet = new DatagramPacket(buffer, buffer.length); socket.receive(packet); if (!LogReplayer.getInstance().isReplaying()) { if (packet.getAddress().getAddress()[0] != 10) { queue.add(new SPLStandardMessagePackage("10.0." + team + "." + buffer[5], team, buffer)); } else { queue.add(new SPLStandardMessagePackage(packet.getAddress().getHostAddress(), team, buffer)); } } buffer = new byte[SPLStandardMessage.SIZE]; } catch (SocketTimeoutException e) { } catch (IOException e) { Log.error("something went wrong while receiving the message packages: " + e.getMessage()); } } } } private static SPLStandardMessageReceiver instance; private static final int MAX_TEAMNUMBER = 100; private final ReceiverThread[] receivers = new ReceiverThread[MAX_TEAMNUMBER]; private final LinkedBlockingQueue<SPLStandardMessagePackage> queue = new LinkedBlockingQueue<>(); /** * Constructor. * * @throws IOException if a problem occurs while creating the receiver * threads */ public SPLStandardMessageReceiver() throws IOException { // Create receiver threads for (int i = 0; i < MAX_TEAMNUMBER; i++) { receivers[i] = new ReceiverThread(i + 1); } } /** * Returns the only instance of the SPLStandardMessageReceiver. * * @return instance */ public static SPLStandardMessageReceiver getInstance() { if (instance == null) { try { instance = new SPLStandardMessageReceiver(); } catch (IOException ex) { JOptionPane.showMessageDialog(null, "Error while setting up packet listeners: " + ex.getMessage(), "IOException", JOptionPane.ERROR_MESSAGE); System.exit(-1); } } return instance; } @Override public void run() { try { // Start receivers for (final ReceiverThread receiver : receivers) { receiver.start(); } // Handle received packages while (!isInterrupted()) { final SPLStandardMessagePackage p = queue.take(); final SPLStandardMessage message; final Class<? extends SPLStandardMessage> c = PluginLoader.getInstance().getMessageClass(p.team); // Log package Logger.getInstance().log(p); // Handle message try { message = c.newInstance(); message.fromByteArray(ByteBuffer.wrap(p.message)); if (message.teamNumValid && message.teamNum != p.team) { message.teamNumValid = false; message.valid = false; } SPLStandardMessage m = message; if (message instanceof AdvancedMessage && message.valid) { try { ((AdvancedMessage) message).init(); } catch (final Throwable e) { m = SPLStandardMessage.createFrom(message); Log.error(e.getClass().getSimpleName() + " was thrown while initializing custom message class " + c.getSimpleName() + ": " + e.getMessage()); } } GameState.getInstance().receiveMessage(p.host, m.teamNumValid ? m.teamNum : p.team, m); } catch (InstantiationException | IllegalAccessException ex) { Log.error("a problem occured while instantiating custom message class " + c.getSimpleName() + ": " + ex.getMessage()); } Thread.yield(); } } catch (InterruptedException ex) { } finally { for (final ReceiverThread receiver : receivers) { receiver.interrupt(); } try { for (final ReceiverThread receiver : receivers) { receiver.join(); } } catch (InterruptedException ex) { } } } /** * Adds the given package to the queue in order to be processed. * * @param p package */ public void addToPackageQueue(final SPLStandardMessagePackage p) { queue.add(p); } /** * Removes all pending packages from the queue. */ public void clearPackageQueue() { queue.clear(); try { Thread.sleep(100); } catch (InterruptedException ex) { } } private static int getTeamport(final int teamNumber) { return teamNumber + 10000; } }