diff --git a/src/net/ash/HIDToVPADNetworkClient/gui/GuiInputControls.java b/src/net/ash/HIDToVPADNetworkClient/gui/GuiInputControls.java index 2f54231..47a0da1 100644 --- a/src/net/ash/HIDToVPADNetworkClient/gui/GuiInputControls.java +++ b/src/net/ash/HIDToVPADNetworkClient/gui/GuiInputControls.java @@ -44,6 +44,7 @@ public class GuiInputControls extends JPanel implements ActionListener { private static final String CONNECT = "Connect"; private static final String DISCONNECT = "Disconnect"; + private static final String RECONNECTING = "Reconnecting"; private JButton connectButton; private JTextField ipTextBox; @@ -93,10 +94,15 @@ public class GuiInputControls extends JPanel implements ActionListener { int delay = 100; //milliseconds ActionListener taskPerformer = new ActionListener() { public void actionPerformed(ActionEvent evt) { - if(NetworkManager.getInstance().isConnected()){ + if(NetworkManager.getInstance().isReconnecting()){ + connectButton.setText(RECONNECTING); + connectButton.setEnabled(false); + }else if(NetworkManager.getInstance().isConnected()){ connectButton.setText(DISCONNECT); + connectButton.setEnabled(true); }else{ connectButton.setText(CONNECT); + connectButton.setEnabled(true); } } }; @@ -127,10 +133,14 @@ public class GuiInputControls extends JPanel implements ActionListener { public void actionPerformed(ActionEvent e) { SwingUtilities.invokeLater(new Runnable() { public void run() { - if(NetworkManager.getInstance().isConnected()){ - NetworkManager.getInstance().disconnect(); - }else{ - NetworkManager.getInstance().connect(ipTextBox.getText()); + if(NetworkManager.getInstance().isReconnecting()){ + + }else{ + if(NetworkManager.getInstance().isConnected()){ + NetworkManager.getInstance().disconnect(); + }else{ + NetworkManager.getInstance().connect(ipTextBox.getText()); + } } } }); diff --git a/src/net/ash/HIDToVPADNetworkClient/manager/ActiveControllerManager.java b/src/net/ash/HIDToVPADNetworkClient/manager/ActiveControllerManager.java index 217f549..bea48ed 100644 --- a/src/net/ash/HIDToVPADNetworkClient/manager/ActiveControllerManager.java +++ b/src/net/ash/HIDToVPADNetworkClient/manager/ActiveControllerManager.java @@ -129,4 +129,13 @@ public class ActiveControllerManager implements Runnable{ } } } + + public void attachAllActiveControllers() { + synchronized (activeControllers) { + for(Entry entry : activeControllers.entrySet()){ + NetworkHIDDevice device = entry.getValue(); + device.sendAttach(); + } + } + } } diff --git a/src/net/ash/HIDToVPADNetworkClient/manager/ControllerManager.java b/src/net/ash/HIDToVPADNetworkClient/manager/ControllerManager.java index 8f5606b..832cc51 100644 --- a/src/net/ash/HIDToVPADNetworkClient/manager/ControllerManager.java +++ b/src/net/ash/HIDToVPADNetworkClient/manager/ControllerManager.java @@ -65,7 +65,10 @@ public class ControllerManager{ connectedDevices.putAll(detectWindowsControllers()); } - //TODO: Enable HID4Java again + /*TODO: Enable HID4Java again. Currently it's disabled because + * We can either use the WiiU directly OR have XInput anyway. + * Adding an option menu for enabling it? + */ //connectedDevices.putAll(detectHIDDevices()); //Remove detached devices @@ -100,9 +103,15 @@ public class ControllerManager{ //e.printStackTrace(); } break; + /* + * TODO: + * Currently the XInput will be set active automatically. + * But this should move to something for the settings? + */ case XINPUT14: try { c = new XInput14Controller(deviceIdentifier); + c.setActive(true); } catch (ControllerInitializationFailedException e) { //e.printStackTrace(); } @@ -110,6 +119,7 @@ public class ControllerManager{ case XINPUT13: try { c = new XInput13Controller(deviceIdentifier); + c.setActive(true); } catch (ControllerInitializationFailedException e) { //e.printStackTrace(); } diff --git a/src/net/ash/HIDToVPADNetworkClient/network/NetworkManager.java b/src/net/ash/HIDToVPADNetworkClient/network/NetworkManager.java index 0091574..5152b9a 100644 --- a/src/net/ash/HIDToVPADNetworkClient/network/NetworkManager.java +++ b/src/net/ash/HIDToVPADNetworkClient/network/NetworkManager.java @@ -44,6 +44,9 @@ public class NetworkManager implements Runnable{ private UDPClient udpClient = null; private static NetworkManager instance = null; + + private List ownCommands = new ArrayList<>(); + private NetworkManager() { } @@ -80,8 +83,8 @@ public class NetworkManager implements Runnable{ int i = 0; while(true){ proccessCommands(); - Utilities.sleep(10); - if(i++ > 1000/10){ + Utilities.sleep(10);//TODO: move magic value to Settings + if(i++ > 1000/10){//TODO: move magic value to Settings ping(); i = 0; } @@ -89,11 +92,13 @@ public class NetworkManager implements Runnable{ } private void ping() { - if(isConnected())sendingCommand(new PingCommand()); + if(isConnected() || tcpClient.isShouldRetry())sendingCommand(new PingCommand()); } - + public void proccessCommands(){ List commands = new ArrayList<>(); + commands.addAll(ownCommands); //TODO: Does this need a synchronized block? It _should_ be only access from this thread. Need to think about it + ownCommands.clear(); synchronized (toRemove) { synchronized (devices) { for(NetworkHIDDevice device : getDevices()){ @@ -135,15 +140,30 @@ public class NetworkManager implements Runnable{ } private void sendingCommand(DeviceCommand command) { - if(command instanceof AttachCommand){ - sendAttach((AttachCommand) command); - }else if(command instanceof DetachCommand){ - sendDetach((DetachCommand) command); - }else if(command instanceof PingCommand){ - sendPing((PingCommand) command); + boolean result = false; + if(isConnected() || tcpClient.isShouldRetry()){ + if(command instanceof AttachCommand){ + result = sendAttach((AttachCommand) command); + }else if(command instanceof DetachCommand){ + result = sendDetach((DetachCommand) command); + }else if(command instanceof PingCommand){ + sendPing((PingCommand) command); + result = true; + }else{ + log.info("UNKNOWN COMMAND!"); + result = true; + } + }else{ + Utilities.sleep(500); //TODO: move magic value to Settings + } + + //Add the command again on errors + if(!result){ + addCommand(command); } } + //TODO: PONG from WiiU? Hey Quark ;) private void sendPing(PingCommand command) { if(sendTCP(Protocol.getRawPingDataToSend(command))){ log.info("PING"); @@ -152,27 +172,32 @@ public class NetworkManager implements Runnable{ } } - private void sendDetach(DetachCommand command) { + private boolean sendDetach(DetachCommand command) { byte[] sendData; try { sendData = Protocol.getRawDetachDataToSend(command); - if(!sendTCP(sendData)){ + if(sendTCP(sendData)){ + log.info("Success detach command for device (" + command.getSender() + ") sent!"); + }else{ log.info("Sending the detach command for device (" + command.getSender() + ") failed. sendTCP failed"); + return false; } } catch (IOException e) { e.printStackTrace(); + return false; } - + return true; } - private void sendAttach(AttachCommand command) { + //TODO: Maybe move it into the Protocol class? + private boolean sendAttach(AttachCommand command) { //Send the TCP command byte[] sendData = null; try { sendData = Protocol.getRawAttachDataToSend(command); }catch (IOException e) { log.info("Building the attach command for device (" + command.getSender() + ") failed." + e.getMessage()); - return; + return false; } if(sendTCP(sendData)){ byte configFound = 0; @@ -182,13 +207,14 @@ public class NetworkManager implements Runnable{ e1.printStackTrace(); } if(configFound == Protocol.TCP_CMD_ATTACH_CONFIG_FOUND){ - log.info("Config on the console found!"); + //log.info("Config on the console found!"); }else if(configFound == Protocol.TCP_CMD_ATTACH_CONFIG_NOT_FOUND){ log.info("NO CONFIG FOUND."); - return; + return false; }else if (configFound == 0){ log.info("Failed to get byte."); disconnect(); + return false; } byte userDataOkay = 0; @@ -198,13 +224,14 @@ public class NetworkManager implements Runnable{ e1.printStackTrace(); } if(userDataOkay == Protocol.TCP_CMD_ATTACH_USERDATA_OKAY){ - log.info("userdata okay!"); + //log.info("userdata okay!"); }else if(userDataOkay == Protocol.TCP_CMD_ATTACH_USERDATA_BAD){ log.info("USERDATA BAD."); - return; + return false; }else if (userDataOkay == 0){ log.info("Failed to get byte."); disconnect(); + return false; } //We receive our device slot and pad slot @@ -215,11 +242,13 @@ public class NetworkManager implements Runnable{ padslot = recvTCPByte(); } catch (IOException e) { log.info("Recieving data after sending a attach failed for device (" + command.getSender() + ") failed." + e.getMessage()); + return false; } if(deviceslot < 0 || padslot < 0){ log.info("Recieving data after sending a attach failed for device (" + command.getSender() + ") failed. We need to disconnect =("); disconnect(); + return false; } //Let's save them for later. @@ -229,10 +258,13 @@ public class NetworkManager implements Runnable{ sender.setPadslot(padslot); }else{ log.info("Something really went wrong. Got an attach event with out an " + NetworkHIDDevice.class.getSimpleName()); + return false; } log.info("Attaching done!"); + return true; }else{ log.info("Sending the attach command for device (" + command.getSender() + ") failed. sendTCP failed"); + return false; } } @@ -268,7 +300,6 @@ public class NetworkManager implements Runnable{ tcpClient.send(rawCommand); result = true; } catch (Exception e) { - // result = false; } } @@ -276,7 +307,7 @@ public class NetworkManager implements Runnable{ } public void disconnect() { - ControllerManager.deactivateAllAttachedControllers(); + //ControllerManager.deactivateAllAttachedControllers(); tcpClient.abort(); } @@ -287,7 +318,6 @@ public class NetworkManager implements Runnable{ private byte recvTCPByte() throws IOException { return tcpClient.recvByte(); } - public boolean isConnected() { return (tcpClient != null && tcpClient.isConnected()); @@ -308,4 +338,12 @@ public class NetworkManager implements Runnable{ } return result; } + + public void addCommand(DeviceCommand command) { + this.ownCommands.add(command); + } + + public boolean isReconnecting() { + return !tcpClient.isConnected() && tcpClient.isShouldRetry(); + } } diff --git a/src/net/ash/HIDToVPADNetworkClient/network/TCPClient.java b/src/net/ash/HIDToVPADNetworkClient/network/TCPClient.java index 39c1026..1e70239 100644 --- a/src/net/ash/HIDToVPADNetworkClient/network/TCPClient.java +++ b/src/net/ash/HIDToVPADNetworkClient/network/TCPClient.java @@ -29,51 +29,69 @@ import java.net.Socket; import java.nio.ByteBuffer; import java.util.concurrent.SynchronousQueue; +import lombok.AccessLevel; import lombok.Getter; +import lombok.Setter; +import lombok.extern.java.Log; +import net.ash.HIDToVPADNetworkClient.manager.ActiveControllerManager; import net.ash.HIDToVPADNetworkClient.network.Protocol.HandshakeReturnCode; import net.ash.HIDToVPADNetworkClient.util.HandleFoundry; +import net.ash.HIDToVPADNetworkClient.util.Settings; +@Log public class TCPClient { private Socket sock; private DataInputStream in; private DataOutputStream out; @Getter private int clientID = HandleFoundry.next(); + @Getter @Setter(AccessLevel.PRIVATE) + private int shouldRetry = Settings.MAXIMUM_TRIES_FOR_RECONNECTING; + private String ip; public TCPClient() { } public synchronized void connect(String ip) throws Exception { - this.ip = ip; + sock = new Socket(); - sock.connect(new InetSocketAddress(ip, Protocol.TCP_PORT), 30000); + sock.connect(new InetSocketAddress(ip, Protocol.TCP_PORT), 2000); in = new DataInputStream(sock.getInputStream()); out = new DataOutputStream(sock.getOutputStream()); - if(doHandshake() == HandshakeReturnCode.BAD_HANDSHAKE){ - throw new Exception(); - } + HandshakeReturnCode resultHandshake = doHandshake(); + if(resultHandshake == HandshakeReturnCode.BAD_HANDSHAKE){ + log.info("[TCP] Handshaking failed"); + throw new Exception(); + }else{ + if(resultHandshake == HandshakeReturnCode.NEW_CLIENT && this.ip != null){ + //We check the IP to be sure it's the first time we connect to a WiiU. //TODO: Sending a ID from the WiiU which will be compared? + //we are new to the client. + ActiveControllerManager.getInstance().attachAllActiveControllers(); + }else if(resultHandshake == HandshakeReturnCode.SAME_CLIENT){ + + } + this.ip = ip; + shouldRetry = 0; + } } private synchronized HandshakeReturnCode doHandshake() throws Exception { - if (recvByte() != Protocol.TCP_HANDSHAKE) return HandshakeReturnCode.BAD_HANDSHAKE; - + if (recvByte() != Protocol.TCP_HANDSHAKE) return HandshakeReturnCode.BAD_HANDSHAKE; send(clientID); - System.out.println("clientID:" + clientID); - - System.out.println("[TCP] Handshaking..."); + log.info("[TCP] Handshaking..."); HandshakeReturnCode test = (recvByte() == Protocol.TCP_NEW_CLIENT) ? HandshakeReturnCode.NEW_CLIENT : HandshakeReturnCode.SAME_CLIENT; - System.out.println(test); return test; } public synchronized boolean abort(){ try { + shouldRetry = Settings.MAXIMUM_TRIES_FOR_RECONNECTING; sock.close(); clientID = HandleFoundry.next(); } catch (IOException e) { - System.out.println(e.getMessage()); + System.out.println(e.getMessage()); //TODO: handle return false; } return true; @@ -85,10 +103,15 @@ public class TCPClient { out.flush(); }catch(IOException e){ try { - connect(ip); //TODO: this is for reconnecting when the WiiU switches the application. But this breaks disconnecting, woops. + if(shouldRetry++ < Settings.MAXIMUM_TRIES_FOR_RECONNECTING){ + System.out.println("Trying again to connect! Attempt number " + shouldRetry); + connect(ip); //TODO: this is for reconnecting when the WiiU switches the application. But this breaks disconnecting, woops. + }else{ + abort(); + } } catch (Exception e1) { - e1.printStackTrace(); - } + //e1.printStackTrace(); + } throw e; } } @@ -118,4 +141,8 @@ public class TCPClient { public synchronized boolean isConnected(){ return (sock != null && sock.isConnected() && !sock.isClosed()); } + + public boolean isShouldRetry() { + return this.shouldRetry < Settings.MAXIMUM_TRIES_FOR_RECONNECTING; + } } diff --git a/src/net/ash/HIDToVPADNetworkClient/util/Settings.java b/src/net/ash/HIDToVPADNetworkClient/util/Settings.java index 569aa02..41ce03c 100644 --- a/src/net/ash/HIDToVPADNetworkClient/util/Settings.java +++ b/src/net/ash/HIDToVPADNetworkClient/util/Settings.java @@ -24,6 +24,7 @@ package net.ash.HIDToVPADNetworkClient.util; public class Settings { public static final int DETECT_CONTROLLER_INTERVAL = 1000; public static final int HANDLE_INPUTS_INTERVAL = 15; + public static final int MAXIMUM_TRIES_FOR_RECONNECTING = 10; private Settings(){}