From 8197db4d86c81b821a30af41d289713ab54fc79d Mon Sep 17 00:00:00 2001 From: Maschell Date: Mon, 10 Apr 2017 15:43:54 +0200 Subject: [PATCH] Updated the TCP Handshake to also negotiate a protocol version. +Formatting etc. --- .../gui/GuiInputControls.java | 3 - .../HIDToVPADNetworkClient/hid/HidDevice.java | 11 ++- .../hid/HidManager.java | 17 ++-- .../hid/hid4java/Hid4JavaHidDevice.java | 2 +- .../hid/purejavahid/PureJavaHidDevice.java | 6 ++ .../network/NetworkManager.java | 20 ++-- .../network/Protocol.java | 92 ++++++++++++++++++- .../network/TCPClient.java | 18 ++-- .../HIDToVPADNetworkClient/util/Settings.java | 1 + 9 files changed, 137 insertions(+), 33 deletions(-) diff --git a/src/net/ash/HIDToVPADNetworkClient/gui/GuiInputControls.java b/src/net/ash/HIDToVPADNetworkClient/gui/GuiInputControls.java index 0f2608f..570d2b5 100644 --- a/src/net/ash/HIDToVPADNetworkClient/gui/GuiInputControls.java +++ b/src/net/ash/HIDToVPADNetworkClient/gui/GuiInputControls.java @@ -41,8 +41,6 @@ import lombok.Getter; import net.ash.HIDToVPADNetworkClient.manager.ControllerManager; import net.ash.HIDToVPADNetworkClient.network.NetworkManager; import net.ash.HIDToVPADNetworkClient.util.Settings; -import java.awt.BorderLayout; -import java.awt.CardLayout; public final class GuiInputControls extends JPanel { private static final long serialVersionUID = 1L; @@ -132,7 +130,6 @@ public final class GuiInputControls extends JPanel { add(Box.createRigidArea(new Dimension(1, 4))); add(autoActivateWrap); add(Box.createVerticalGlue()); - add(Box.createVerticalGlue()); diff --git a/src/net/ash/HIDToVPADNetworkClient/hid/HidDevice.java b/src/net/ash/HIDToVPADNetworkClient/hid/HidDevice.java index 722000d..9d6fb38 100644 --- a/src/net/ash/HIDToVPADNetworkClient/hid/HidDevice.java +++ b/src/net/ash/HIDToVPADNetworkClient/hid/HidDevice.java @@ -25,42 +25,47 @@ package net.ash.HIDToVPADNetworkClient.hid; public interface HidDevice { /** * Opens the HidDevice for usage. + * * @return true on success, false when it failed. */ boolean open(); - + /** * Closes the HidDevice */ void close(); - + /** * Returns the VendorID of the HidDevice + * * @return vendorID */ short getVendorId(); /** * Returns the ProductID of the HidDevice + * * @return productID */ short getProductId(); - /** * Returns the latest data. + * * @return An byte array containing the latest data. If no data is present, it'll return a empty byte array */ byte[] getLatestData(); /** * Retuns the Usage of this HID-Device + * * @return usage */ short getUsage(); /** * Returns the path of this HidDevice + * * @return path */ String getPath(); diff --git a/src/net/ash/HIDToVPADNetworkClient/hid/HidManager.java b/src/net/ash/HIDToVPADNetworkClient/hid/HidManager.java index b613351..5ee6d49 100644 --- a/src/net/ash/HIDToVPADNetworkClient/hid/HidManager.java +++ b/src/net/ash/HIDToVPADNetworkClient/hid/HidManager.java @@ -37,12 +37,13 @@ public class HidManager { public static HidDevice getDeviceByPath(String path) throws IOException { return backend.getDeviceByPath(path); } - + public static List getAttachedControllers() { List connectedGamepads = new ArrayList(); for (HidDevice info : backend.enumerateDevices()) { if (isGamepad(info)) { + // Skip Xbox controller under windows. We should use XInput instead. if (isXboxController(info) && Settings.isWindows()) { continue; @@ -61,17 +62,17 @@ public class HidManager { private static boolean isPlaystationController(HidDevice info) { if (info == null) return false; - return (info.getVendorId() == 0x054c); + return (info.getVendorId() == (short) 0x054c); } private static boolean isNintendoController(HidDevice info) { if (info == null) return false; - return (info.getVendorId() == 0x57e); + return (info.getVendorId() == (short) 0x57e); } private static boolean isXboxController(HidDevice info) { if (info == null) return false; - return (info.getVendorId() == 0x045e) && ((info.getProductId() == 0x02ff) || (info.getProductId() == 0x02a1)); + return (info.getVendorId() == (short) 0x045e) && ((info.getProductId() == (short) 0x02ff) || (info.getProductId() == (short) 0x02a1)); } static { @@ -81,13 +82,13 @@ public class HidManager { backend = new PureJavaHidManagerBackend(); } else if (Settings.isLinux()) { backend = new Hid4JavaHidManagerBackend(); - } else{ + } else { backend = null; } log.info("Plattform: " + System.getProperty("os.name")); - if(backend != null){ - log.info("Backend: " + backend.getClass().getSimpleName()); - }else{ + if (backend != null) { + log.info("Backend: " + backend.getClass().getSimpleName()); + } else { log.info("No Backend loaded =("); } } diff --git a/src/net/ash/HIDToVPADNetworkClient/hid/hid4java/Hid4JavaHidDevice.java b/src/net/ash/HIDToVPADNetworkClient/hid/hid4java/Hid4JavaHidDevice.java index 9d9c661..ad6104f 100644 --- a/src/net/ash/HIDToVPADNetworkClient/hid/hid4java/Hid4JavaHidDevice.java +++ b/src/net/ash/HIDToVPADNetworkClient/hid/hid4java/Hid4JavaHidDevice.java @@ -68,7 +68,7 @@ class Hid4JavaHidDevice implements HidDevice { @Override public String toString() { - return "Hid4JavaHidDevice [myDevice=" + myDevice + ", data=" + Arrays.toString(data) + "]"; + return "Hid4JavaHidDevice [vid= " + getVendorId() + ", pid= " + getProductId() + ", data=" + Arrays.toString(data) + "]"; } @Override diff --git a/src/net/ash/HIDToVPADNetworkClient/hid/purejavahid/PureJavaHidDevice.java b/src/net/ash/HIDToVPADNetworkClient/hid/purejavahid/PureJavaHidDevice.java index 2dff246..d4b822d 100644 --- a/src/net/ash/HIDToVPADNetworkClient/hid/purejavahid/PureJavaHidDevice.java +++ b/src/net/ash/HIDToVPADNetworkClient/hid/purejavahid/PureJavaHidDevice.java @@ -89,4 +89,10 @@ class PureJavaHidDevice implements HidDevice, InputReportListener { public String getPath() { return myDeviceInfo.getPath(); } + + @Override + public String toString() { + return "PureJavaHidDevice [vid= " + String.format("%04X", getVendorId()) + ", pid= " + String.format("%04X", getProductId()) + ", path= " + getPath() + + ", data=" + Arrays.toString(currentData) + "]"; + } } diff --git a/src/net/ash/HIDToVPADNetworkClient/network/NetworkManager.java b/src/net/ash/HIDToVPADNetworkClient/network/NetworkManager.java index a26c0c8..b21e5bf 100644 --- a/src/net/ash/HIDToVPADNetworkClient/network/NetworkManager.java +++ b/src/net/ash/HIDToVPADNetworkClient/network/NetworkManager.java @@ -31,6 +31,7 @@ import lombok.Synchronized; import lombok.extern.java.Log; import net.ash.HIDToVPADNetworkClient.controller.Controller; import net.ash.HIDToVPADNetworkClient.manager.ActiveControllerManager; +import net.ash.HIDToVPADNetworkClient.network.Protocol.HandshakeReturnCode; import net.ash.HIDToVPADNetworkClient.util.MessageBox; import net.ash.HIDToVPADNetworkClient.util.MessageBoxManager; import net.ash.HIDToVPADNetworkClient.util.Settings; @@ -163,7 +164,7 @@ public final class NetworkManager implements Runnable { try { pong = tcpClient.recvByte(); if (pong == Protocol.TCP_CMD_PONG) { - log.info("Ping...Pong!"); + if (Settings.DEBUG_TCP_PING_PONG) log.info("Ping...Pong!"); } else { log.info("Got no valid response to a Ping. Disconnecting."); disconnect(); @@ -344,13 +345,18 @@ public final class NetworkManager implements Runnable { boolean result = false; log.info("Trying to connect to: " + ip); try { - tcpClient.connect(ip); - log.info("TCP Connected!"); - udpClient = UDPClient.createUDPClient(ip); - if (udpClient != null) { - result = true; + HandshakeReturnCode tcpresult = tcpClient.connect(ip); + if (tcpresult == HandshakeReturnCode.GOOD_HANDSHAKE) { + log.info("TCP Connected!"); + udpClient = UDPClient.createUDPClient(ip); + if (udpClient != null) { + result = true; + } + } else { + String error = "Error while connecting."; + log.info(error); } - } catch (Exception e) { + } catch (IOException e) { String error = "Error while connecting: " + e.getMessage(); log.info(error); MessageBoxManager.addMessageBox(error, MessageBox.MESSAGE_WARNING); diff --git a/src/net/ash/HIDToVPADNetworkClient/network/Protocol.java b/src/net/ash/HIDToVPADNetworkClient/network/Protocol.java index 9c36f37..f8e90ee 100644 --- a/src/net/ash/HIDToVPADNetworkClient/network/Protocol.java +++ b/src/net/ash/HIDToVPADNetworkClient/network/Protocol.java @@ -28,13 +28,22 @@ import java.nio.ByteBuffer; import java.util.List; import lombok.extern.java.Log; +import net.ash.HIDToVPADNetworkClient.manager.ActiveControllerManager; +import net.ash.HIDToVPADNetworkClient.util.MessageBox; +import net.ash.HIDToVPADNetworkClient.util.MessageBoxManager; @Log final class Protocol { + private static ProtocolVersion currentProtocol = ProtocolVersion.MY_VERSION; static final int TCP_PORT = 8112; static final int UDP_PORT = 8113; - static final byte TCP_HANDSHAKE = 0x12; + static final byte TCP_HANDSHAKE_VERSION_1 = 0x12; + static final byte TCP_HANDSHAKE_VERSION_2 = 0x13; + static final byte TCP_HANDSHAKE_ABORT = 0x30; + + static final byte TCP_HANDSHAKE = TCP_HANDSHAKE_VERSION_2; // default version. + static final byte TCP_SAME_CLIENT = 0x20; static final byte TCP_NEW_CLIENT = 0x21; @@ -96,4 +105,85 @@ final class Protocol { return bos.toByteArray(); } + public static HandshakeReturnCode doHandshake(TCPClient tcpClient) throws IOException { + Protocol.currentProtocol = ProtocolVersion.MY_VERSION; + HandshakeReturnCode resultHandshake = HandshakeReturnCode.BAD_HANDSHAKE; + ProtocolVersion wiiuProtocolVersion = ProtocolVersion.getEnumByValue(tcpClient.recvByte()); + + /** + * Handshake 1. At first the server (WiiU) sends his protocol version 2. The network clients answers with his preferred version (which needs to be + * equals or lower the version the server (WiiU) sent him) or an abort command. 3a. If the client sent a abort, close the connection and wait for + * another connection 3b. If the client sent his highest supported version, the server confirm that he is able to use this version (by sending the + * version back) or sending a abort command to disconnect. + **/ + + if (wiiuProtocolVersion == ProtocolVersion.UNKOWN) { + log.info("Connected to a WiiU with an unsupported protocol version. It may support an older though"); + tcpClient.send(ProtocolVersion.MY_VERSION.getVersionByte()); + } else if (wiiuProtocolVersion == ProtocolVersion.Version1) { + // Do nothing. + } else if (wiiuProtocolVersion == ProtocolVersion.Version2) { + // We want to do version 2! + tcpClient.send(ProtocolVersion.Version2.getVersionByte()); + } + + if (wiiuProtocolVersion == ProtocolVersion.Version1) { + Protocol.currentProtocol = ProtocolVersion.Version1; + resultHandshake = HandshakeReturnCode.GOOD_HANDSHAKE; + } else { + ProtocolVersion finalProtocolVersion = ProtocolVersion.getEnumByValue(tcpClient.recvByte()); + if (finalProtocolVersion == ProtocolVersion.Abort) { + String message = "The HIDtoVPAD version is not supported. Try to use the newest version of HIDtoVPAD and this network client."; + log.info(message); + MessageBoxManager.addMessageBox(message, MessageBox.MESSAGE_ERROR); + resultHandshake = HandshakeReturnCode.BAD_HANDSHAKE; + } else if (finalProtocolVersion == ProtocolVersion.UNKOWN) { + String message = "Something stranged happend while connecting. Try to use the newest version of HIDtoVPAD and this network client."; + log.info(message); + MessageBoxManager.addMessageBox(message, MessageBox.MESSAGE_ERROR); + resultHandshake = HandshakeReturnCode.BAD_HANDSHAKE; + } else { + Protocol.currentProtocol = finalProtocolVersion; + resultHandshake = HandshakeReturnCode.GOOD_HANDSHAKE; + } + } + + if (resultHandshake == HandshakeReturnCode.GOOD_HANDSHAKE) { + ActiveControllerManager.getInstance().attachAllActiveControllers(); + log.info("Handshake was successful! Using protocol version " + (currentProtocol.getVersionByte() - TCP_HANDSHAKE_VERSION_1 + 1)); + return resultHandshake; + } else { + log.info("[TCP] Handshaking failed"); + return resultHandshake; + } + // return false; + } + + public enum ProtocolVersion { + MY_VERSION((byte) TCP_HANDSHAKE), Version1((byte) TCP_HANDSHAKE_VERSION_1), Version2((byte) TCP_HANDSHAKE_VERSION_2), Abort( + (byte) TCP_HANDSHAKE_ABORT), UNKOWN((byte) 0x00); + private final byte versionByte; + + private ProtocolVersion(byte versionByte) { + this.versionByte = versionByte; + } + + public static ProtocolVersion getEnumByValue(byte value) { + switch (value) { + case TCP_HANDSHAKE_VERSION_1: + return ProtocolVersion.Version1; + case TCP_HANDSHAKE_VERSION_2: + return ProtocolVersion.Version2; + case TCP_HANDSHAKE_ABORT: + return ProtocolVersion.Abort; + default: + return ProtocolVersion.UNKOWN; + } + } + + public byte getVersionByte() { + return versionByte; + } + } + } diff --git a/src/net/ash/HIDToVPADNetworkClient/network/TCPClient.java b/src/net/ash/HIDToVPADNetworkClient/network/TCPClient.java index 4b59fd4..2bbdf30 100644 --- a/src/net/ash/HIDToVPADNetworkClient/network/TCPClient.java +++ b/src/net/ash/HIDToVPADNetworkClient/network/TCPClient.java @@ -33,7 +33,6 @@ import lombok.Getter; import lombok.Setter; import lombok.Synchronized; import lombok.extern.java.Log; -import net.ash.HIDToVPADNetworkClient.manager.ActiveControllerManager; import net.ash.HIDToVPADNetworkClient.network.Protocol.HandshakeReturnCode; import net.ash.HIDToVPADNetworkClient.util.Settings; @@ -53,23 +52,18 @@ final class TCPClient { } @Synchronized("lock") - void connect(String ip) throws Exception { + HandshakeReturnCode connect(String ip) throws IOException { sock = new Socket(); sock.connect(new InetSocketAddress(ip, Protocol.TCP_PORT), 2000); in = new DataInputStream(sock.getInputStream()); out = new DataOutputStream(sock.getOutputStream()); - HandshakeReturnCode resultHandshake = HandshakeReturnCode.GOOD_HANDSHAKE; - if (recvByte() != Protocol.TCP_HANDSHAKE) resultHandshake = HandshakeReturnCode.BAD_HANDSHAKE; - - if (resultHandshake == HandshakeReturnCode.GOOD_HANDSHAKE) { - ActiveControllerManager.getInstance().attachAllActiveControllers(); + HandshakeReturnCode result = Protocol.doHandshake(this); + if (result == HandshakeReturnCode.GOOD_HANDSHAKE) { this.ip = ip; shouldRetry = 0; - } else { - log.info("[TCP] Handshaking failed"); - throw new Exception(); } + return result; } @Synchronized("lock") @@ -112,6 +106,10 @@ final class TCPClient { send(ByteBuffer.allocate(4).putInt(value).array()); } + public void send(byte _byte) throws IOException { + send(ByteBuffer.allocate(1).put(_byte).array()); + } + @Synchronized("lock") byte recvByte() throws IOException { return in.readByte(); diff --git a/src/net/ash/HIDToVPADNetworkClient/util/Settings.java b/src/net/ash/HIDToVPADNetworkClient/util/Settings.java index fec12c0..74295fa 100644 --- a/src/net/ash/HIDToVPADNetworkClient/util/Settings.java +++ b/src/net/ash/HIDToVPADNetworkClient/util/Settings.java @@ -49,6 +49,7 @@ public final class Settings { public static boolean SCAN_AUTOMATICALLY_FOR_CONTROLLERS = true; public static boolean DEBUG_UDP_OUTPUT = false; + public static boolean DEBUG_TCP_PING_PONG = false; public static boolean SEND_DATA_ONLY_ON_CHANGE = false; public static boolean AUTO_ACTIVATE_CONTROLLER = false;