From cab2d36f483ac5f0f60e4eec556b4a56a9cc87e5 Mon Sep 17 00:00:00 2001 From: Valentin Heiserer <73257760+Vale54321@users.noreply.github.com> Date: Fri, 19 Apr 2024 16:15:51 +0200 Subject: [PATCH] refactored a lot and added possibility to play games on DedicatedServer (#43) --- Arduino/nfcReader/nfcReader.ino | 3 + Backend/pom.xml | 2 +- .../java/org/schafkopf/BackendServer.java | 46 ++++---- .../schafkopf/DedicatedServerConnection.java | 101 ++++++++++++++++++ .../java/org/schafkopf/FrontendEndpoint.java | 11 +- .../java/org/schafkopf/HeartbeatSender.java | 26 +++++ .../schafkopf/cardreader/UsbCardReader.java | 58 ++++++---- .../org/schafkopf/player/LocalPlayer.java | 32 +++++- Backend/schafkopf-server/pom.xml | 2 +- .../java/org/schafkopf/ClientConnection.java | 75 +++++++++++++ .../java/org/schafkopf/DedicatedServer.java | 94 ++++++++++++++++ .../schafkopf/FrontendEndpointCreator.java | 23 ++++ .../main/java/org/schafkopf/GameSession.java | 19 ++++ .../src/main/java/org/schafkopf/Main.java | 18 ---- .../main/java/org/schafkopf/GameState.java | 0 .../java/org/schafkopf/MessageSender.java | 6 ++ .../main/java/org/schafkopf/Schafkopf.java | 74 ++++--------- .../main/java/org/schafkopf/Spielablauf.java | 0 .../java/org/schafkopf/player/BotPlayer.java | 0 .../java/org/schafkopf/player/Player.java | 0 .../spielcontroller/FarbGeierController.java | 0 .../spielcontroller/FarbSoloController.java | 0 .../spielcontroller/FarbWenzController.java | 0 .../spielcontroller/GeierController.java | 0 .../spielcontroller/GeierWenzController.java | 0 .../spielcontroller/SauSpielController.java | 0 .../spielcontroller/SoloController.java | 0 .../spielcontroller/SpielController.java | 0 .../spielcontroller/StandardController.java | 0 .../spielcontroller/WenzController.java | 0 Frontend/src/App.vue | 57 +++++----- 31 files changed, 492 insertions(+), 155 deletions(-) create mode 100644 Backend/schafkopf-client/src/main/java/org/schafkopf/DedicatedServerConnection.java create mode 100644 Backend/schafkopf-client/src/main/java/org/schafkopf/HeartbeatSender.java create mode 100644 Backend/schafkopf-server/src/main/java/org/schafkopf/ClientConnection.java create mode 100644 Backend/schafkopf-server/src/main/java/org/schafkopf/DedicatedServer.java create mode 100644 Backend/schafkopf-server/src/main/java/org/schafkopf/FrontendEndpointCreator.java create mode 100644 Backend/schafkopf-server/src/main/java/org/schafkopf/GameSession.java delete mode 100644 Backend/schafkopf-server/src/main/java/org/schafkopf/Main.java rename Backend/{schafkopf-client => schafkopf-shared}/src/main/java/org/schafkopf/GameState.java (100%) create mode 100644 Backend/schafkopf-shared/src/main/java/org/schafkopf/MessageSender.java rename Backend/{schafkopf-client => schafkopf-shared}/src/main/java/org/schafkopf/Schafkopf.java (67%) rename Backend/{schafkopf-client => schafkopf-shared}/src/main/java/org/schafkopf/Spielablauf.java (100%) rename Backend/{schafkopf-client => schafkopf-shared}/src/main/java/org/schafkopf/player/BotPlayer.java (100%) rename Backend/{schafkopf-client => schafkopf-shared}/src/main/java/org/schafkopf/player/Player.java (100%) rename Backend/{schafkopf-client => schafkopf-shared}/src/main/java/org/schafkopf/spielcontroller/FarbGeierController.java (100%) rename Backend/{schafkopf-client => schafkopf-shared}/src/main/java/org/schafkopf/spielcontroller/FarbSoloController.java (100%) rename Backend/{schafkopf-client => schafkopf-shared}/src/main/java/org/schafkopf/spielcontroller/FarbWenzController.java (100%) rename Backend/{schafkopf-client => schafkopf-shared}/src/main/java/org/schafkopf/spielcontroller/GeierController.java (100%) rename Backend/{schafkopf-client => schafkopf-shared}/src/main/java/org/schafkopf/spielcontroller/GeierWenzController.java (100%) rename Backend/{schafkopf-client => schafkopf-shared}/src/main/java/org/schafkopf/spielcontroller/SauSpielController.java (100%) rename Backend/{schafkopf-client => schafkopf-shared}/src/main/java/org/schafkopf/spielcontroller/SoloController.java (100%) rename Backend/{schafkopf-client => schafkopf-shared}/src/main/java/org/schafkopf/spielcontroller/SpielController.java (100%) rename Backend/{schafkopf-client => schafkopf-shared}/src/main/java/org/schafkopf/spielcontroller/StandardController.java (100%) rename Backend/{schafkopf-client => schafkopf-shared}/src/main/java/org/schafkopf/spielcontroller/WenzController.java (100%) diff --git a/Arduino/nfcReader/nfcReader.ino b/Arduino/nfcReader/nfcReader.ino index 655b140..f9c5273 100644 --- a/Arduino/nfcReader/nfcReader.ino +++ b/Arduino/nfcReader/nfcReader.ino @@ -17,6 +17,9 @@ void setup(void) { } nfc.SAMConfig(); + + // Send a marker string to identify the device + Serial.println("Adafruit PN532 NFC Marker"); } void loop(void) { diff --git a/Backend/pom.xml b/Backend/pom.xml index 174beb4..de9f408 100644 --- a/Backend/pom.xml +++ b/Backend/pom.xml @@ -66,7 +66,7 @@ - org.schafkopf.BackendServer + org.schafkopf.DedicatedServer diff --git a/Backend/schafkopf-client/src/main/java/org/schafkopf/BackendServer.java b/Backend/schafkopf-client/src/main/java/org/schafkopf/BackendServer.java index 0eb37e0..0ae9ca1 100644 --- a/Backend/schafkopf-client/src/main/java/org/schafkopf/BackendServer.java +++ b/Backend/schafkopf-client/src/main/java/org/schafkopf/BackendServer.java @@ -1,6 +1,5 @@ package org.schafkopf; -import com.google.gson.JsonObject; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer; @@ -25,9 +24,12 @@ import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlets.CrossOriginFilter; import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer; import org.schafkopf.cardreader.UsbCardReader; +import org.schafkopf.player.BotPlayer; +import org.schafkopf.player.LocalPlayer; +import org.schafkopf.player.Player; /** Main Class that represents the Backend Server. */ -public class BackendServer { +public class BackendServer implements MessageSender { private final Server server; private final ServerConnector connector; private CountDownLatch nfcLatch = new CountDownLatch(1); @@ -39,6 +41,8 @@ public class BackendServer { private final List frontendEndpoints = new ArrayList<>(); + private DedicatedServerConnection dedicatedServerConnection; + /** Creates an Instance of the Backend Server. */ public BackendServer() throws URISyntaxException, IOException { server = new Server(); @@ -48,7 +52,9 @@ public class BackendServer { connector.setPort(address.getPort()); server.addConnector(connector); - schafkopfGame = new Schafkopf(this); + schafkopfGame = new Schafkopf(new Player[]{new BotPlayer(), new LocalPlayer(this), + new LocalPlayer(this), + new LocalPlayer(this)}, this); new UsbCardReader(this); @@ -86,6 +92,13 @@ public class BackendServer { startHttpServer(); URI uri = new URI("http://localhost:8081"); // Replace with your target URL Desktop.getDesktop().browse(uri); + + startDedicatedServerConnectionThread(); + } + + private void startDedicatedServerConnectionThread() { + dedicatedServerConnection = new DedicatedServerConnection(this); + dedicatedServerConnection.connect(); } private void startHttpServer() { @@ -147,7 +160,6 @@ public class BackendServer { /** The main entrypoint of the Application. */ public static void main(String[] args) throws Exception { BackendServer server = new BackendServer(); - server.setPort(8080); server.start(); server.join(); } @@ -166,10 +178,6 @@ public class BackendServer { context.addFilter(cors, "*", types); } - private void setPort(int port) { - connector.setPort(port); - } - private void start() throws Exception { server.start(); } @@ -191,23 +199,12 @@ public class BackendServer { * * @param message Message to send (String). */ - public void sendMessageToAllFrontendEndpoints(String message) { + private void sendMessageToAllFrontendEndpoints(String message) { for (FrontendEndpoint endpoint : frontendEndpoints) { endpoint.sendMessage(message); } } - /** - * Sends Message to all Frontend Instances. - * - * @param message Message to send (JsonObject). - */ - public void sendMessageToAllFrontendEndpoints(JsonObject message) { - for (FrontendEndpoint endpoint : frontendEndpoints) { - endpoint.sendMessage(message.toString()); - } - } - /** method to call to wait for NFC input. */ public String waitForCardScan() throws InterruptedException { this.readingMode = true; @@ -234,4 +231,13 @@ public class BackendServer { this.uidString = uidString; nfcLatch.countDown(); } + + public void startDedicatedServerGame() { + dedicatedServerConnection.start(); + } + + @Override + public void sendMessage(String message) { + sendMessageToAllFrontendEndpoints(message); + } } diff --git a/Backend/schafkopf-client/src/main/java/org/schafkopf/DedicatedServerConnection.java b/Backend/schafkopf-client/src/main/java/org/schafkopf/DedicatedServerConnection.java new file mode 100644 index 0000000..c4069f6 --- /dev/null +++ b/Backend/schafkopf-client/src/main/java/org/schafkopf/DedicatedServerConnection.java @@ -0,0 +1,101 @@ +package org.schafkopf; + +import java.net.URI; +import java.util.concurrent.CountDownLatch; +import org.eclipse.jetty.websocket.api.Session; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; +import org.eclipse.jetty.websocket.api.annotations.WebSocket; +import org.eclipse.jetty.websocket.client.ClientUpgradeRequest; +import org.eclipse.jetty.websocket.client.WebSocketClient; + +/** Main Class that represents the Backend Server. */ +@WebSocket +public class DedicatedServerConnection { + + private final MessageSender messageSender; + private final CountDownLatch closeLatch; + private static Session session; + + public DedicatedServerConnection(MessageSender messageSender) { + this.messageSender = messageSender; + this.closeLatch = new CountDownLatch(1); + } + + @OnWebSocketConnect + public void onConnect(Session session) { + this.session = session; + System.out.println("Connected to server."); + } + + @OnWebSocketMessage + public void onMessage(String message) { + messageSender.sendMessage(message); + System.out.println("Received message from server: " + message); + } + + @OnWebSocketClose + public void onClose(int statusCode, String reason) { + System.out.println("Connection closed: " + reason); + closeLatch.countDown(); + } + + @OnWebSocketError + public void onError(Throwable cause) { + System.err.println("Error occurred: " + cause.getMessage()); + } + + /** Main Class that represents the Backend Server. */ + public static void sendMessage(String message) { + try { + session.getRemote().sendString(message); + System.out.println("Sent message to server: " + message); + } catch (Exception e) { + System.err.println("Error sending message: " + e.getMessage()); + } + } + + public void awaitClose() throws InterruptedException { + closeLatch.await(); + } + + /** Main Class that represents the Backend Server. */ + public void connect() { + Thread connectionThread = new Thread(() -> { + try { + String serverUri = "ws://localhost:8085/"; + WebSocketClient client = new WebSocketClient(); + try { + client.start(); + //DedicatedServerConnection socketClient = new DedicatedServerConnection(); + HeartbeatSender heartbeatSender = new HeartbeatSender(this); + heartbeatSender.start(); // Start sending heartbeat messages + URI uri = new URI(serverUri); + ClientUpgradeRequest request = new ClientUpgradeRequest(); + client.connect(this, uri, request); + + System.out.println("Connecting to : " + uri); + this.awaitClose(); + } catch (Exception e) { + System.err.println("Error connecting to server: " + e.getMessage()); + } finally { + try { + client.stop(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } catch (Exception e) { + System.err.println("Error starting dedicated server connection: " + e.getMessage()); + } + }); + connectionThread.start(); + start(); + } + + public static void start() { + sendMessage("START_GAME"); + } +} diff --git a/Backend/schafkopf-client/src/main/java/org/schafkopf/FrontendEndpoint.java b/Backend/schafkopf-client/src/main/java/org/schafkopf/FrontendEndpoint.java index 37da97b..103087d 100644 --- a/Backend/schafkopf-client/src/main/java/org/schafkopf/FrontendEndpoint.java +++ b/Backend/schafkopf-client/src/main/java/org/schafkopf/FrontendEndpoint.java @@ -37,17 +37,10 @@ public class FrontendEndpoint extends WebSocketAdapter { backendServer.schafkopfGame.stopGame(); } - if (message.contains("showtrumpf")) { - backendServer.schafkopfGame.showTrumpf(); + if (message.contains("startdedicated")) { + backendServer.startDedicatedServerGame(); } - if (message.contains("showfarben")) { - backendServer.schafkopfGame.showFarbe(); - } - - if (message.contains("setgame")) { - backendServer.schafkopfGame.setGame(message); - } } @Override diff --git a/Backend/schafkopf-client/src/main/java/org/schafkopf/HeartbeatSender.java b/Backend/schafkopf-client/src/main/java/org/schafkopf/HeartbeatSender.java new file mode 100644 index 0000000..a7cb3ba --- /dev/null +++ b/Backend/schafkopf-client/src/main/java/org/schafkopf/HeartbeatSender.java @@ -0,0 +1,26 @@ +package org.schafkopf; + +import java.util.Timer; +import java.util.TimerTask; + +/** Creates an Instance of the Backend Server. */ +public class HeartbeatSender { + private static final int HEARTBEAT_INTERVAL = 15000; // 1 minute + + private final DedicatedServerConnection client; + + public HeartbeatSender(DedicatedServerConnection client) { + this.client = client; + } + + /** Creates an Instance of the Backend Server. */ + public void start() { + Timer timer = new Timer(); + timer.scheduleAtFixedRate(new TimerTask() { + @Override + public void run() { + client.sendMessage("HEARTBEAT SYN"); // Send a heartbeat message + } + }, HEARTBEAT_INTERVAL, HEARTBEAT_INTERVAL); + } +} diff --git a/Backend/schafkopf-client/src/main/java/org/schafkopf/cardreader/UsbCardReader.java b/Backend/schafkopf-client/src/main/java/org/schafkopf/cardreader/UsbCardReader.java index 6f0e8c0..62f235a 100644 --- a/Backend/schafkopf-client/src/main/java/org/schafkopf/cardreader/UsbCardReader.java +++ b/Backend/schafkopf-client/src/main/java/org/schafkopf/cardreader/UsbCardReader.java @@ -1,6 +1,7 @@ package org.schafkopf.cardreader; import com.fazecast.jSerialComm.SerialPort; +import com.sun.tools.jconsole.JConsoleContext; import io.github.cdimascio.dotenv.Dotenv; import java.io.UnsupportedEncodingException; import org.schafkopf.BackendServer; @@ -10,7 +11,7 @@ public class UsbCardReader extends CardReader { private volatile boolean isRunning = true; Dotenv dotenv = Dotenv.configure().directory("./").load(); - private final String comPort = dotenv.get("COM_PORT"); + private String comPort = null; /** * Creates an Instance of the KartenLeser. @@ -19,7 +20,6 @@ public class UsbCardReader extends CardReader { */ public UsbCardReader(BackendServer server) { super(server); - new Thread(this::run).start(); } @@ -27,29 +27,46 @@ public class UsbCardReader extends CardReader { isRunning = false; } - /** run the reader. */ + /** Run the reader. */ public void run() { - SerialPort[] ports = SerialPort.getCommPorts(); - SerialPort selectedPort = null; + // SerialPort[] ports = SerialPort.getCommPorts(); + // + // for (SerialPort port : ports) { + // if (port.openPort()) { + // System.out.println(port.getSystemPortName()); + // try { + // Thread.sleep(5000); + // } catch (InterruptedException e) { + // throw new RuntimeException(e); + // } + // // Read any data available on the serial port + // byte[] initialBuffer = new byte[port.bytesAvailable()]; + // int initialBytesRead = port.readBytes(initialBuffer, initialBuffer.length); + // String initialData = null; + // try { + // initialData = new String(initialBuffer, 0, initialBytesRead, "UTF-8").trim(); + // } catch (UnsupportedEncodingException e) { + // throw new RuntimeException(e); + // } + // System.out.print("Raw data: "); + // for (byte b : initialBuffer) { + // System.out.print(b + " "); + // } + // System.out.println(initialData); + // if (initialData.contains("Adafruit PN532 NFC Marker")) { + // comPort = port.getSystemPortName(); + // } + // } + // } - for (SerialPort port : ports) { - if (port.getSystemPortName().equals(this.comPort)) { - selectedPort = port; - break; - } - } + comPort = dotenv.get("COM_PORT"); - if (selectedPort == null) { - System.out.println(this.comPort + " not found"); + if (comPort == null) { + System.out.println("Adafruit PN532 NFC device not found"); return; } - if (ports.length == 0) { - System.out.println("No serial ports found"); - return; - } - - SerialPort serialPort = selectedPort; // You may need to adjust this based on your setup + SerialPort serialPort = SerialPort.getCommPort(comPort); serialPort.setBaudRate(115200); if (serialPort.openPort()) { @@ -60,8 +77,9 @@ public class UsbCardReader extends CardReader { if (serialPort.bytesAvailable() > 0) { byte[] buffer = new byte[serialPort.bytesAvailable()]; int bytesRead = serialPort.readBytes(buffer, buffer.length); - String data = new String(buffer, 0, bytesRead, "UTF-8").trim(); + + // Process the received data server.nfcGelesen(data); } diff --git a/Backend/schafkopf-client/src/main/java/org/schafkopf/player/LocalPlayer.java b/Backend/schafkopf-client/src/main/java/org/schafkopf/player/LocalPlayer.java index 844d602..b543a94 100644 --- a/Backend/schafkopf-client/src/main/java/org/schafkopf/player/LocalPlayer.java +++ b/Backend/schafkopf-client/src/main/java/org/schafkopf/player/LocalPlayer.java @@ -1,8 +1,9 @@ package org.schafkopf.player; -import org.schafkopf.Schafkopf; +import org.schafkopf.BackendServer; import org.schafkopf.karte.Karte; import org.schafkopf.karte.KartenListe; +import org.schafkopf.karte.KartenUtil; import org.schafkopf.spielcontroller.SpielController; /** @@ -10,14 +11,35 @@ import org.schafkopf.spielcontroller.SpielController; */ public class LocalPlayer extends Player { - private final Schafkopf schafkopf; + private final BackendServer server; - public LocalPlayer(Schafkopf schafkopf) { - this.schafkopf = schafkopf; + public LocalPlayer(BackendServer server) { + this.server = server; } @Override public Karte play(SpielController spiel, KartenListe tischKarten, KartenListe gespielteKarten) { - return schafkopf.wartetAufKarte(); + return wartetAufKarte(); + } + + /** Waits for a Card and returns a Karte Object. */ + private Karte wartetAufKarte() { + String uid = null; + System.out.println("Starte Warten auf Karte"); + try { + uid = server.waitForCardScan(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + Karte karte = KartenUtil.getIdOfUid(uid); + + if (karte == null) { + System.out.println("Ungültige Karte"); + return wartetAufKarte(); + } + System.out.println("Karte gescannt: " + karte.getName()); + System.out.println("Beende Warten auf Karte"); + return karte; } } diff --git a/Backend/schafkopf-server/pom.xml b/Backend/schafkopf-server/pom.xml index 7741f9e..acd62be 100644 --- a/Backend/schafkopf-server/pom.xml +++ b/Backend/schafkopf-server/pom.xml @@ -36,7 +36,7 @@ - org.schafkopf.Main + org.schafkopf.DedicatedServer diff --git a/Backend/schafkopf-server/src/main/java/org/schafkopf/ClientConnection.java b/Backend/schafkopf-server/src/main/java/org/schafkopf/ClientConnection.java new file mode 100644 index 0000000..0038b52 --- /dev/null +++ b/Backend/schafkopf-server/src/main/java/org/schafkopf/ClientConnection.java @@ -0,0 +1,75 @@ +package org.schafkopf; + +import java.io.IOException; +import java.util.concurrent.CountDownLatch; +import org.eclipse.jetty.websocket.api.Session; +import org.eclipse.jetty.websocket.api.WebSocketAdapter; +import org.schafkopf.player.BotPlayer; +import org.schafkopf.player.Player; + +/** Class that represents one Frontend Connection. */ +public class ClientConnection extends WebSocketAdapter implements MessageSender { + private final CountDownLatch closureLatch = new CountDownLatch(1); + private DedicatedServer dedicatedServer; + + private Session session; + + public ClientConnection(DedicatedServer dedicatedServer) { + this.dedicatedServer = dedicatedServer; + System.out.println("new ClientConnection created."); + } + + @Override + public void onWebSocketConnect(Session session) { + this.session = session; + super.onWebSocketConnect(session); + String clientIp = session.getRemoteAddress().toString(); + System.out.println("Endpoint connected from ip: " + clientIp); + + dedicatedServer.addFrontendEndpoint(this); + } + + @Override + public void onWebSocketText(String message) { + super.onWebSocketText(message); + if (message.equals("HEARTBEAT SYN")) { + System.out.println("Received HEARTBEAT message from " + session.getRemoteAddress() + "."); + sendMessage("HEARTBEAT ACK"); + return; + } + if (message.equals("START_GAME")) { + System.out.println("Received START_GAME message from " + session.getRemoteAddress() + "."); + dedicatedServer.addGameSession(new GameSession(new Schafkopf(new Player[] { + new BotPlayer(), new BotPlayer(), new BotPlayer(), new BotPlayer() + }, this))); + return; + } + System.out.println("Received TEXT message:" + message); + } + + @Override + public void onWebSocketClose(int statusCode, String reason) { + super.onWebSocketClose(statusCode, reason); + + dedicatedServer.removeFrontendEndpoint(this); + + System.out.println("Socket Closed: [" + statusCode + "] " + reason); + closureLatch.countDown(); + } + + @Override + public void onWebSocketError(Throwable cause) { + super.onWebSocketError(cause); + cause.printStackTrace(System.err); + } + + /** send a Message to the connected FrontEnd. */ + @Override + public void sendMessage(String message) { + try { + getRemote().sendString(message); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/Backend/schafkopf-server/src/main/java/org/schafkopf/DedicatedServer.java b/Backend/schafkopf-server/src/main/java/org/schafkopf/DedicatedServer.java new file mode 100644 index 0000000..d86fe8e --- /dev/null +++ b/Backend/schafkopf-server/src/main/java/org/schafkopf/DedicatedServer.java @@ -0,0 +1,94 @@ +package org.schafkopf; + +import jakarta.servlet.DispatcherType; +import java.net.InetSocketAddress; +import java.time.Duration; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.servlet.FilterHolder; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlets.CrossOriginFilter; +import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer; + +/** Main Class that represents the Backend Server. */ +public class DedicatedServer { + private final Server server; + private final ServerConnector connector; + + private final List clientConnections = new ArrayList<>(); + + private final List gameSessions = new ArrayList<>(); + + /** Creates an Instance of the Backend Server. */ + public DedicatedServer() { + server = new Server(); + InetSocketAddress address = new InetSocketAddress("localhost", 8085); + connector = new ServerConnector(server); + connector.setHost(address.getHostName()); + connector.setPort(address.getPort()); + server.addConnector(connector); + + // Setup the basic application "context" for this application at "/" + // This is also known as the handler tree (in jetty speak) + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + context.setContextPath("/"); + server.setHandler(context); + // Configure CORS settings + configureCors(context); + // Configure specific websocket behavior + JettyWebSocketServletContainerInitializer.configure( + context, + (servletContext, wsContainer) -> { + // Configure default max size + wsContainer.setMaxTextMessageSize(65535); + wsContainer.setIdleTimeout(Duration.ofDays(300000)); + // Add websockets + wsContainer.addMapping("/*", new FrontendEndpointCreator(this)); + }); + } + + /** The main entrypoint of the Application. */ + public static void main(String[] args) throws Exception { + DedicatedServer server = new DedicatedServer(); + server.start(); + server.join(); + } + + private void configureCors(ServletContextHandler context) { + // Enable CORS for all paths + FilterHolder cors = context.addFilter(CrossOriginFilter.class, "/*", null); + + // Configure allowed origins, headers, and methods + cors.setInitParameter("allowedOrigins", "*"); + cors.setInitParameter("allowedHeaders", "X-Requested-With,Content-Type,Accept,Origin"); + cors.setInitParameter("allowedMethods", "GET,POST,PUT,DELETE,OPTIONS"); + + // Add filter mappings + EnumSet types = EnumSet.of(DispatcherType.REQUEST); + context.addFilter(cors, "*", types); + } + + private void start() throws Exception { + server.start(); + } + + private void join() throws InterruptedException { + server.join(); + } + + public void addFrontendEndpoint(ClientConnection endpoint) { + clientConnections.add(endpoint); + } + + public void removeFrontendEndpoint(ClientConnection endpoint) { + clientConnections.remove(endpoint); + } + + + public void addGameSession(GameSession gameSession) { + gameSessions.add(gameSession); + } +} diff --git a/Backend/schafkopf-server/src/main/java/org/schafkopf/FrontendEndpointCreator.java b/Backend/schafkopf-server/src/main/java/org/schafkopf/FrontendEndpointCreator.java new file mode 100644 index 0000000..fe498b7 --- /dev/null +++ b/Backend/schafkopf-server/src/main/java/org/schafkopf/FrontendEndpointCreator.java @@ -0,0 +1,23 @@ +package org.schafkopf; + +import org.eclipse.jetty.websocket.server.JettyServerUpgradeRequest; +import org.eclipse.jetty.websocket.server.JettyServerUpgradeResponse; +import org.eclipse.jetty.websocket.server.JettyWebSocketCreator; + +/** + * Creater to make new Instances of the FrontendConnection. + */ +public class FrontendEndpointCreator implements JettyWebSocketCreator { + private DedicatedServer dedicatedServer; + + public FrontendEndpointCreator(DedicatedServer dedicatedServer) { + this.dedicatedServer = dedicatedServer; + } + + @Override + public Object createWebSocket( + JettyServerUpgradeRequest jettyServerUpgradeRequest, + JettyServerUpgradeResponse jettyServerUpgradeResponse) { + return new ClientConnection(this.dedicatedServer); + } +} diff --git a/Backend/schafkopf-server/src/main/java/org/schafkopf/GameSession.java b/Backend/schafkopf-server/src/main/java/org/schafkopf/GameSession.java new file mode 100644 index 0000000..5e38e25 --- /dev/null +++ b/Backend/schafkopf-server/src/main/java/org/schafkopf/GameSession.java @@ -0,0 +1,19 @@ +package org.schafkopf; + +/** The main entrypoint of the Application. */ +public class GameSession { + + private Schafkopf schafkopf; + + /** The main entrypoint of the Application. */ + public GameSession(Schafkopf schafkopf) { + this.schafkopf = schafkopf; + System.out.println("new GameSession created."); + startGame(); + } + + private void startGame() { + schafkopf.startGame(); + } + +} diff --git a/Backend/schafkopf-server/src/main/java/org/schafkopf/Main.java b/Backend/schafkopf-server/src/main/java/org/schafkopf/Main.java deleted file mode 100644 index f117247..0000000 --- a/Backend/schafkopf-server/src/main/java/org/schafkopf/Main.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.schafkopf; - -import org.schafkopf.karte.Karte; -import org.schafkopf.karte.KartenListe; -import org.schafkopf.karte.KartenUtil; - -/** Creates an Instance of the Backend Server. */ -public class Main { - /** Creates an Instance of the Backend Server. */ - public static void main(String[] args) { - - System.out.println("Hello and welcome!"); - KartenListe testHand = KartenUtil.zieheZufallsHand(8); - for (Karte karte : testHand.getKartenListe()) { - System.out.println(karte.getName()); - } - } -} \ No newline at end of file diff --git a/Backend/schafkopf-client/src/main/java/org/schafkopf/GameState.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/GameState.java similarity index 100% rename from Backend/schafkopf-client/src/main/java/org/schafkopf/GameState.java rename to Backend/schafkopf-shared/src/main/java/org/schafkopf/GameState.java diff --git a/Backend/schafkopf-shared/src/main/java/org/schafkopf/MessageSender.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/MessageSender.java new file mode 100644 index 0000000..a40037a --- /dev/null +++ b/Backend/schafkopf-shared/src/main/java/org/schafkopf/MessageSender.java @@ -0,0 +1,6 @@ +package org.schafkopf; + +/** The main entrypoint of the Application. */ +public interface MessageSender { + void sendMessage(String message); +} diff --git a/Backend/schafkopf-client/src/main/java/org/schafkopf/Schafkopf.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/Schafkopf.java similarity index 67% rename from Backend/schafkopf-client/src/main/java/org/schafkopf/Schafkopf.java rename to Backend/schafkopf-shared/src/main/java/org/schafkopf/Schafkopf.java index 8494250..15bac2f 100644 --- a/Backend/schafkopf-client/src/main/java/org/schafkopf/Schafkopf.java +++ b/Backend/schafkopf-shared/src/main/java/org/schafkopf/Schafkopf.java @@ -6,7 +6,6 @@ import org.schafkopf.karte.KartenFarbe; import org.schafkopf.karte.KartenListe; import org.schafkopf.karte.KartenUtil; import org.schafkopf.player.BotPlayer; -import org.schafkopf.player.LocalPlayer; import org.schafkopf.player.Player; import org.schafkopf.spielcontroller.FarbGeierController; import org.schafkopf.spielcontroller.FarbSoloController; @@ -18,14 +17,12 @@ import org.schafkopf.spielcontroller.WenzController; /** The main class representing the Schafkopf game. */ public class Schafkopf { - private final BackendServer server; + private final MessageSender messageSender; /** The game controller. This is the class that implements the game logic. */ private SpielController spiel = new SauSpielController(0, KartenFarbe.EICHEL); - private final Player[] player = { - new BotPlayer(), new LocalPlayer(this), new LocalPlayer(this), new LocalPlayer(this) - }; + private final Player[] player; private GameState gameState = new GameState(GamePhase.GAME_STOP); private Thread spielThread; @@ -33,10 +30,11 @@ public class Schafkopf { /** * Constructor for the Schafkopf class. * - * @param server The backend server associated with the game. + * @param messageSender MessageSender */ - Schafkopf(BackendServer server) { - this.server = server; + public Schafkopf(Player[] player, MessageSender messageSender) { + this.player = player; + this.messageSender = messageSender; System.out.println("SchaffKopfGame erstellt"); } @@ -44,62 +42,26 @@ public class Schafkopf { return player; } - /** Sends all Trumpf Karten of the current GameType to the Frontend. */ - public void showTrumpf() { - server.sendMessageToAllFrontendEndpoints(spiel.getTrumpfKarten().getJson()); - } - - /** Sends all Farb Karten of the current GameType to the Frontend. */ - public void showFarbe() { - server.sendMessageToAllFrontendEndpoints(spiel.getFarbKarten().getJson()); - } - - /** Waits for a Card and returns a Karte Object. */ - public Karte wartetAufKarte() { - String uid = null; - System.out.println("Starte Warten auf Karte"); - try { - uid = server.waitForCardScan(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - - Karte karte = KartenUtil.getIdOfUid(uid); - - if (karte == null) { - System.out.println("Ungültige Karte"); - return wartetAufKarte(); - } - System.out.println("Karte gescannt: " + karte.getName()); - System.out.println("Beende Warten auf Karte"); - return karte; - } - /** Set GameState to "started" and start Game Thread. */ public void startGame() { if (gameState.getGamePhase() != GamePhase.GAME_STOP) { System.out.println("Game already started!"); - server.sendMessageToAllFrontendEndpoints("Game already started!"); + messageSender.sendMessage("Game already started!"); } else { gameState = new GameState(GamePhase.GAME_START); setAndSendGameState(gameState); System.out.println("Start Game"); - // KartenListe botHand = KartenUtil.zieheZufallsHand(8); - KartenListe botHand = new KartenListe(); - botHand.addKarten(Karte.EICHEL_7); - botHand.addKarten(Karte.SCHELL_7); - botHand.addKarten(Karte.BLATT_7); - - botHand.addKarten(Karte.EICHEL_X); - botHand.addKarten(Karte.HERZ_X); - botHand.addKarten(Karte.HERZ_7); - - botHand.addKarten(Karte.EICHEL_U); - botHand.addKarten(Karte.EICHEL_O); + KartenListe austeilen = KartenUtil.initializeSchafKopfCardDeck(); for (Player currentPlayer : player) { if (currentPlayer instanceof BotPlayer botPlayer) { - // Perform actions specific to BotPlayer + KartenListe botHand = new KartenListe(); + for (int i = 7; i >= 0; i--) { + System.out.println("Austeilen: " + austeilen.size()); + System.out.println("Bot Hand: " + i); + botHand.addKarten(austeilen.removeKarten(austeilen.getByIndex(i))); + } + System.out.println("Bot Hand: " + botHand.getJson().toString()); botPlayer.setCards(botHand); // Replace with the actual method you want to call } } @@ -114,7 +76,7 @@ public class Schafkopf { public void stopGame() { if (gameState.getGamePhase() == GamePhase.GAME_STOP) { System.out.println("no active Game!"); - server.sendMessageToAllFrontendEndpoints("no active Game!"); + messageSender.sendMessage("no active Game!"); } else { gameState = new GameState(GamePhase.GAME_STOP); setAndSendGameState(gameState); @@ -126,7 +88,7 @@ public class Schafkopf { /** Set GameType. */ public void setGame(String message) { System.out.println("Set Game: " + message); - server.sendMessageToAllFrontendEndpoints("Set Game: " + message); + messageSender.sendMessage("Set Game: " + message); switch (message) { case "setgame:herzsolo": this.spiel = new FarbSoloController(0, KartenFarbe.HERZ); @@ -184,7 +146,7 @@ public class Schafkopf { public void setAndSendGameState(GameState gameState) { this.gameState = gameState; - this.server.sendMessageToAllFrontendEndpoints(this.gameState.getJson()); + this.messageSender.sendMessage(this.gameState.getJson().toString()); } public GameState getGameState() { diff --git a/Backend/schafkopf-client/src/main/java/org/schafkopf/Spielablauf.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/Spielablauf.java similarity index 100% rename from Backend/schafkopf-client/src/main/java/org/schafkopf/Spielablauf.java rename to Backend/schafkopf-shared/src/main/java/org/schafkopf/Spielablauf.java diff --git a/Backend/schafkopf-client/src/main/java/org/schafkopf/player/BotPlayer.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/player/BotPlayer.java similarity index 100% rename from Backend/schafkopf-client/src/main/java/org/schafkopf/player/BotPlayer.java rename to Backend/schafkopf-shared/src/main/java/org/schafkopf/player/BotPlayer.java diff --git a/Backend/schafkopf-client/src/main/java/org/schafkopf/player/Player.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/player/Player.java similarity index 100% rename from Backend/schafkopf-client/src/main/java/org/schafkopf/player/Player.java rename to Backend/schafkopf-shared/src/main/java/org/schafkopf/player/Player.java diff --git a/Backend/schafkopf-client/src/main/java/org/schafkopf/spielcontroller/FarbGeierController.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/FarbGeierController.java similarity index 100% rename from Backend/schafkopf-client/src/main/java/org/schafkopf/spielcontroller/FarbGeierController.java rename to Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/FarbGeierController.java diff --git a/Backend/schafkopf-client/src/main/java/org/schafkopf/spielcontroller/FarbSoloController.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/FarbSoloController.java similarity index 100% rename from Backend/schafkopf-client/src/main/java/org/schafkopf/spielcontroller/FarbSoloController.java rename to Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/FarbSoloController.java diff --git a/Backend/schafkopf-client/src/main/java/org/schafkopf/spielcontroller/FarbWenzController.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/FarbWenzController.java similarity index 100% rename from Backend/schafkopf-client/src/main/java/org/schafkopf/spielcontroller/FarbWenzController.java rename to Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/FarbWenzController.java diff --git a/Backend/schafkopf-client/src/main/java/org/schafkopf/spielcontroller/GeierController.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/GeierController.java similarity index 100% rename from Backend/schafkopf-client/src/main/java/org/schafkopf/spielcontroller/GeierController.java rename to Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/GeierController.java diff --git a/Backend/schafkopf-client/src/main/java/org/schafkopf/spielcontroller/GeierWenzController.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/GeierWenzController.java similarity index 100% rename from Backend/schafkopf-client/src/main/java/org/schafkopf/spielcontroller/GeierWenzController.java rename to Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/GeierWenzController.java diff --git a/Backend/schafkopf-client/src/main/java/org/schafkopf/spielcontroller/SauSpielController.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/SauSpielController.java similarity index 100% rename from Backend/schafkopf-client/src/main/java/org/schafkopf/spielcontroller/SauSpielController.java rename to Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/SauSpielController.java diff --git a/Backend/schafkopf-client/src/main/java/org/schafkopf/spielcontroller/SoloController.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/SoloController.java similarity index 100% rename from Backend/schafkopf-client/src/main/java/org/schafkopf/spielcontroller/SoloController.java rename to Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/SoloController.java diff --git a/Backend/schafkopf-client/src/main/java/org/schafkopf/spielcontroller/SpielController.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/SpielController.java similarity index 100% rename from Backend/schafkopf-client/src/main/java/org/schafkopf/spielcontroller/SpielController.java rename to Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/SpielController.java diff --git a/Backend/schafkopf-client/src/main/java/org/schafkopf/spielcontroller/StandardController.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/StandardController.java similarity index 100% rename from Backend/schafkopf-client/src/main/java/org/schafkopf/spielcontroller/StandardController.java rename to Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/StandardController.java diff --git a/Backend/schafkopf-client/src/main/java/org/schafkopf/spielcontroller/WenzController.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/WenzController.java similarity index 100% rename from Backend/schafkopf-client/src/main/java/org/schafkopf/spielcontroller/WenzController.java rename to Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/WenzController.java diff --git a/Frontend/src/App.vue b/Frontend/src/App.vue index 748687f..064b083 100644 --- a/Frontend/src/App.vue +++ b/Frontend/src/App.vue @@ -21,6 +21,12 @@ function startSimulation(): void { } +function startDedicated(): void { + sendMessageToServer("startdedicated"); + +} + + function stopSimulation(): void { sendMessageToServer("stopsimulation"); } @@ -131,32 +137,33 @@ onMounted(() => { {{ message }} + + + + + + + + + + + + + + + + + + + + + + + + + - Sauspiel - - - - - - - - - - - - - - - - - Geier - Wenz - - - Zeige alle Farben - Zeige alle Trumpfkarten - - + Dedicated Starten Starten Stoppen