import java.io.IOException; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketTimeoutException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class GameController { // Connection timeout for client sockets in milliseconds public static final int CONNECTION_TIMEOUT = 10 * 1000; public static int semesterToBoardSize(int semester) { return semester + 13; } public HashMap> supportedVersions = new HashMap<>(Map.of( "1.1.0", OnlinePlayer_1_1_0.class )); public void startOnlineGame(Class localPlayerClass, InetSocketAddress address, int size) throws IOException { AsyncSocket clientSocket; boolean localPlayerIsServer = address.getHostName() == null; if (localPlayerIsServer) { // SERVER MODE ServerSocket serverSocket = new ServerSocket(address.getPort()); System.out.println("Waiting for client connection..."); clientSocket = new AsyncSocket(serverSocket.accept(), null); serverSocket.close(); } else { // CLIENT MODE Socket socket = new Socket(); try { socket.connect(address, CONNECTION_TIMEOUT); clientSocket = new AsyncSocket(socket, null); } catch (SocketTimeoutException e) { e.printStackTrace(); throw new RuntimeException("Connection timed out"); } finally { socket.close(); } } clientSocket.send("VERSION", "Gruppe03 " + String.join(" ", supportedVersions.keySet())); clientSocket.setHandler((message) -> { SocketPackage socketPackage = new SocketPackage(message); if (socketPackage.getName().equals("VERSION")) { List opponentSupportedVersions = socketPackage.splitData(); String usedVersion = findMostRecentVersion(new ArrayList(supportedVersions.keySet()), opponentSupportedVersions); if (usedVersion == null) { throw new RuntimeException("No common versions found"); } // instantiate players Class onlinePlayerClass = supportedVersions.get(usedVersion); LocalPlayer localPlayer; OnlinePlayer onlinePlayer; try { localPlayer = localPlayerClass.getDeclaredConstructor().newInstance(); onlinePlayer = onlinePlayerClass.getDeclaredConstructor().newInstance((Integer)size, clientSocket); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("Unable to instantiate players"); } localPlayer.isServer = localPlayerIsServer; onlinePlayer.isServer = !localPlayerIsServer; startGameWithInstancedPlayers(localPlayer, onlinePlayer); } else { throw new RuntimeException("Unexpected Package received before game initialisation"); } }); } /** * finds the largest common version in two lists of version strings * @return null if no common versions are found */ public String findMostRecentVersion(List versions1, List versions2) { if (versions1 == null || versions2 == null) return null; String largestCommonVersion = null; for (String v1 : versions1) { if (!checkVersionString(v1)) continue; for (String v2 : versions2) { if (!checkVersionString(v2)) continue; // strings within the lists must be equal // if no largest common version has been found or currently checked version is larger than // previous largest common version, new largest version has been found if (compareVersions(v1, v2) == 0 && (largestCommonVersion == null || compareVersions(v1, largestCommonVersion) >= 1)) { largestCommonVersion = v1; continue; } } } return largestCommonVersion; } /** * compares two version strings * @return * 0 if versions are equal * 1 if version1 is more recent than version2 * -1 otherwise */ public int compareVersions(String version1, String version2) { if (!checkVersionString(version1) || !checkVersionString(version2)) { throw new IllegalArgumentException("Version is not valid version string"); } String[] version1Split = version1.split("\\."); String[] version2Split = version2.split("\\."); for (int i = 0; i < version1Split.length; i++) { int v1 = Integer.parseInt(version1Split[i]); int v2 = Integer.parseInt(version2Split[i]); if (v1 == v2) continue; if (v1 > v2) { return 1; } else { return -1; } } return 0; } /** * checks if a provided string matches the format of a version number */ public boolean checkVersionString(String versionString) { return versionString != null && versionString.matches("\\d+\\.\\d+\\.\\d+"); } public void startLocalGame(Class localPlayerClass, Class enemyClass, int size) { LocalPlayer localPlayer; AiPlayer aiPlayer; try { localPlayer = localPlayerClass.getDeclaredConstructor().newInstance(); aiPlayer = enemyClass.getDeclaredConstructor().newInstance(); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("Unable to instantiate players"); } localPlayer.createBoard(size); aiPlayer.createBoard(size); startGameWithInstancedPlayers(localPlayer, aiPlayer); } private void startGameWithInstancedPlayers(LocalPlayer p1, Player p2) { p1.setEnemy(p2); p2.setEnemy(p1); // TODO: frontend configuration } }