diff --git a/Backend/pom.xml b/Backend/pom.xml index de9f408..6322fd7 100644 --- a/Backend/pom.xml +++ b/Backend/pom.xml @@ -135,5 +135,11 @@ io.github.cdimascio 3.0.0 + + + org.json + json + 20240303 + \ No newline at end of file diff --git a/Backend/schafkopf-client/pom.xml b/Backend/schafkopf-client/pom.xml index a474cd5..61979e7 100644 --- a/Backend/schafkopf-client/pom.xml +++ b/Backend/schafkopf-client/pom.xml @@ -41,7 +41,7 @@ - org.schafkopf.BackendServer + org.schafkopf.SchafkopfClient 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 0ae9ca1..200608c 100644 --- a/Backend/schafkopf-client/src/main/java/org/schafkopf/BackendServer.java +++ b/Backend/schafkopf-client/src/main/java/org/schafkopf/BackendServer.java @@ -1,63 +1,44 @@ package org.schafkopf; -import com.sun.net.httpserver.HttpExchange; -import com.sun.net.httpserver.HttpHandler; -import com.sun.net.httpserver.HttpServer; import jakarta.servlet.DispatcherType; import java.awt.Desktop; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.InetSocketAddress; import java.net.URI; -import java.net.URISyntaxException; import java.net.URL; import java.time.Duration; import java.util.ArrayList; import java.util.EnumSet; import java.util.List; -import java.util.concurrent.CountDownLatch; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.servlet.DefaultServlet; import org.eclipse.jetty.servlet.FilterHolder; import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; 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; +import org.schafkopf.SchafkopfMessage.SchafkopfBaseMessage; -/** Main Class that represents the Backend Server. */ +/** + * Main Class that represents the Backend Server. + */ public class BackendServer implements MessageSender { - private final Server server; - private final ServerConnector connector; - private CountDownLatch nfcLatch = new CountDownLatch(1); - private Boolean readingMode = false; - private String uidString = ""; - /** Important variables. */ - public final Schafkopf schafkopfGame; + private final Server server; private final List frontendEndpoints = new ArrayList<>(); - private DedicatedServerConnection dedicatedServerConnection; - - /** Creates an Instance of the Backend Server. */ - public BackendServer() throws URISyntaxException, IOException { + /** + * Creates an Instance of the Backend Server. + */ + public BackendServer(String hostName, int port, boolean openFrontend, + MessageListener messageListener) throws Exception { server = new Server(); - InetSocketAddress address = new InetSocketAddress("localhost", 8080); - connector = new ServerConnector(server); - connector.setHost(address.getHostName()); - connector.setPort(address.getPort()); + + ServerConnector connector = new ServerConnector(server); + connector.setHost(hostName); + connector.setPort(port); server.addConnector(connector); - schafkopfGame = new Schafkopf(new Player[]{new BotPlayer(), new LocalPlayer(this), - new LocalPlayer(this), - new LocalPlayer(this)}, this); - - new UsbCardReader(this); - // 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); @@ -71,11 +52,9 @@ public class BackendServer implements MessageSender { if (webContentUrl == null) { throw new RuntimeException("Unable to find 'web-content' directory"); } - String webContentPath = webContentUrl.toExternalForm(); context.setResourceBase(webContentPath); - - System.out.println("Web Content Path: " + webContentPath); + context.addServlet(new ServletHolder("frontend", DefaultServlet.class), "/"); // Configure specific websocket behavior JettyWebSocketServletContainerInitializer.configure( @@ -85,83 +64,25 @@ public class BackendServer implements MessageSender { wsContainer.setMaxTextMessageSize(65535); wsContainer.setIdleTimeout(Duration.ofDays(300000)); // Add websockets - wsContainer.addMapping("/schafkopf-events/*", new FrontendEndpointCreator(this)); + wsContainer.addMapping("/schafkopf-events/*", + new FrontendEndpointCreator(this, messageListener)); }); - // Integrate simple HTTP server - 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() { - try { - HttpServer httpServer = HttpServer.create(new InetSocketAddress(8081), 0); - httpServer.createContext("/", new MyHandler()); - httpServer.setExecutor(null); - httpServer.start(); - System.out.println("HTTP Server started on port 8081"); - } catch (IOException e) { - e.printStackTrace(); + if (openFrontend) { + URI uri = new URI("http://" + hostName + ":" + port); // Replace with your target URL + Desktop.getDesktop().browse(uri); } - } - - static class MyHandler implements HttpHandler { - @Override - public void handle(HttpExchange t) throws IOException { - String path = t.getRequestURI().getPath(); - if ("/".equals(path)) { - path = "/index.html"; // default to index.html - } + // Start the server in a separate thread + Thread serverThread = new Thread(() -> { try { - InputStream fileStream = - getClass().getClassLoader().getResourceAsStream("web-content" + path); - if (fileStream != null) { - byte[] data = fileStream.readAllBytes(); - // Set the appropriate MIME type for JavaScript files - String mimeType = getMimeType(path); - t.getResponseHeaders().set("Content-Type", mimeType); - t.sendResponseHeaders(200, data.length); - - try (OutputStream os = t.getResponseBody()) { - os.write(data); - } - } else { - // File not found - t.sendResponseHeaders(404, -1); - } - } catch (IOException e) { + server.start(); + server.join(); // Wait for server to finish execution + } catch (Exception e) { e.printStackTrace(); - t.sendResponseHeaders(500, -1); } - } - - private String getMimeType(String path) { - if (path.endsWith(".js")) { - return "application/javascript"; - } else if (path.endsWith(".html")) { - return "text/html"; - } else if (path.endsWith(".css")) { - return "text/css"; - } - // Add more MIME types as needed - return "application/octet-stream"; - } - } - - /** The main entrypoint of the Application. */ - public static void main(String[] args) throws Exception { - BackendServer server = new BackendServer(); - server.start(); - server.join(); + }); + serverThread.start(); } private void configureCors(ServletContextHandler context) { @@ -205,39 +126,14 @@ public class BackendServer implements MessageSender { } } - /** method to call to wait for NFC input. */ - public String waitForCardScan() throws InterruptedException { - this.readingMode = true; - nfcLatch.await(); - Thread.sleep(20); - this.readingMode = false; - nfcLatch = new CountDownLatch(1); - return this.uidString; - } - - /** - * checks uid of scanned card and do nothing if Server is not in reading mode. - * - * @param uidString uid to check. - */ - public void nfcGelesen(String uidString) { - if (this.uidString.equals(uidString)) { - return; - } - if (!this.readingMode) { - return; - } - - this.uidString = uidString; - nfcLatch.countDown(); - } - - public void startDedicatedServerGame() { - dedicatedServerConnection.start(); - } - @Override - public void sendMessage(String message) { + public void sendMessage(SchafkopfBaseMessage message) { + sendMessageToAllFrontendEndpoints( + message.getBaseMessage().toString()); + } + + + public void sendMessageTest(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 index c4069f6..0f6fd5e 100644 --- a/Backend/schafkopf-client/src/main/java/org/schafkopf/DedicatedServerConnection.java +++ b/Backend/schafkopf-client/src/main/java/org/schafkopf/DedicatedServerConnection.java @@ -1,6 +1,8 @@ package org.schafkopf; +import com.google.gson.JsonObject; import java.net.URI; +import java.net.URISyntaxException; import java.util.concurrent.CountDownLatch; import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose; @@ -10,30 +12,58 @@ 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; +import org.schafkopf.SchafkopfMessage.SchafkopfBaseMessage; +import org.schafkopf.SchafkopfMessage.SchafkopfMessageOrigin; +import org.schafkopf.SchafkopfMessage.SchafkopfMessageType; -/** Main Class that represents the Backend Server. */ +/** + * Main Class that represents the Backend Server. + */ @WebSocket -public class DedicatedServerConnection { +public class DedicatedServerConnection implements MessageSender { - private final MessageSender messageSender; + private final MessageListener messageListener; private final CountDownLatch closeLatch; + private final CountDownLatch connectionLatch; private static Session session; - public DedicatedServerConnection(MessageSender messageSender) { - this.messageSender = messageSender; + /** + * Class that represents one Frontend Connection. + */ + public DedicatedServerConnection(String address, MessageListener messageListener) { + URI uri = null; + try { + uri = new URI(address); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + + this.messageListener = messageListener; this.closeLatch = new CountDownLatch(1); + this.connectionLatch = new CountDownLatch(1); + + String host = uri.getHost(); + int port = uri.getPort(); + connect("ws://" + host + ":" + port); + try { + connectionLatch.await(); // Wait until the connection is established + } catch (InterruptedException e) { + System.err.println("Error waiting for connection: " + e.getMessage()); + } } + /** + * Class that represents one Frontend Connection. + */ @OnWebSocketConnect public void onConnect(Session session) { this.session = session; - System.out.println("Connected to server."); + connectionLatch.countDown(); } @OnWebSocketMessage public void onMessage(String message) { - messageSender.sendMessage(message); - System.out.println("Received message from server: " + message); + messageListener.receiveMessage(message); } @OnWebSocketClose @@ -47,10 +77,14 @@ public class DedicatedServerConnection { System.err.println("Error occurred: " + cause.getMessage()); } - /** Main Class that represents the Backend Server. */ - public static void sendMessage(String message) { + /** + * Main Class that represents the Backend Server. + */ + @Override + public void sendMessage(SchafkopfBaseMessage message) { try { - session.getRemote().sendString(message); + session.getRemote().sendString( + new SchafkopfMessage(SchafkopfMessageOrigin.BACKEND, message).getMessageAsString()); System.out.println("Sent message to server: " + message); } catch (Exception e) { System.err.println("Error sending message: " + e.getMessage()); @@ -61,15 +95,15 @@ public class DedicatedServerConnection { closeLatch.await(); } - /** Main Class that represents the Backend Server. */ - public void connect() { + /** + * Main Class that represents the Backend Server. + */ + public void connect(String serverUri) { 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); @@ -95,7 +129,23 @@ public class DedicatedServerConnection { start(); } - public static void start() { - sendMessage("START_GAME"); + /** + * Class that represents one Frontend Connection. + */ + public void start() { + JsonObject messageObject = new JsonObject(); + + messageObject.addProperty("message_type", "START_GAME"); + sendMessage(new SchafkopfBaseMessage(SchafkopfMessageType.START_GAME)); + } + + /** + * Class that represents one Frontend Connection. + */ + public void join() { + JsonObject messageObject = new JsonObject(); + + messageObject.addProperty("message_type", "JOIN_GAME"); + sendMessage(new SchafkopfBaseMessage(SchafkopfMessageType.JOIN_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 103087d..79d2836 100644 --- a/Backend/schafkopf-client/src/main/java/org/schafkopf/FrontendEndpoint.java +++ b/Backend/schafkopf-client/src/main/java/org/schafkopf/FrontendEndpoint.java @@ -5,16 +5,27 @@ import java.util.concurrent.CountDownLatch; import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.WebSocketAdapter; -/** Class that represents one Frontend Connection. */ +/** + * Class that represents one Frontend Connection. + */ public class FrontendEndpoint extends WebSocketAdapter { + private final CountDownLatch closureLatch = new CountDownLatch(1); private BackendServer backendServer; + private final MessageListener messageListener; - public FrontendEndpoint(BackendServer backendServer) { + /** + * Class that represents one Frontend Connection. + */ + public FrontendEndpoint(BackendServer backendServer, MessageListener messageListener) { + this.messageListener = messageListener; this.backendServer = backendServer; System.out.println("new FrontendEndpoint"); } + /** + * Class that represents one Frontend Connection. + */ @Override public void onWebSocketConnect(Session session) { super.onWebSocketConnect(session); @@ -27,20 +38,9 @@ public class FrontendEndpoint extends WebSocketAdapter { @Override public void onWebSocketText(String message) { super.onWebSocketText(message); - System.out.println("Received TEXT message:" + message); - - if (message.contains("startsimulation")) { - backendServer.schafkopfGame.startGame(); + if (messageListener != null) { + messageListener.receiveMessage(message); // Notify the listener } - - if (message.contains("stopsimulation")) { - backendServer.schafkopfGame.stopGame(); - } - - if (message.contains("startdedicated")) { - backendServer.startDedicatedServerGame(); - } - } @Override @@ -59,7 +59,9 @@ public class FrontendEndpoint extends WebSocketAdapter { cause.printStackTrace(System.err); } - /** send a Message to the connected FrontEnd. */ + /** + * send a Message to the connected FrontEnd. + */ public void sendMessage(String message) { try { getRemote().sendString(message); diff --git a/Backend/schafkopf-client/src/main/java/org/schafkopf/FrontendEndpointCreator.java b/Backend/schafkopf-client/src/main/java/org/schafkopf/FrontendEndpointCreator.java index d5538ce..f2f9620 100644 --- a/Backend/schafkopf-client/src/main/java/org/schafkopf/FrontendEndpointCreator.java +++ b/Backend/schafkopf-client/src/main/java/org/schafkopf/FrontendEndpointCreator.java @@ -9,8 +9,10 @@ import org.eclipse.jetty.websocket.server.JettyWebSocketCreator; */ public class FrontendEndpointCreator implements JettyWebSocketCreator { private BackendServer backendServer; + private final MessageListener messageListener; - public FrontendEndpointCreator(BackendServer backendServer) { + public FrontendEndpointCreator(BackendServer backendServer, MessageListener messageListener) { + this.messageListener = messageListener; this.backendServer = backendServer; } @@ -18,6 +20,6 @@ public class FrontendEndpointCreator implements JettyWebSocketCreator { public Object createWebSocket( JettyServerUpgradeRequest jettyServerUpgradeRequest, JettyServerUpgradeResponse jettyServerUpgradeResponse) { - return new FrontendEndpoint(this.backendServer); + return new FrontendEndpoint(this.backendServer, messageListener); } } diff --git a/Backend/schafkopf-client/src/main/java/org/schafkopf/HeartbeatSender.java b/Backend/schafkopf-client/src/main/java/org/schafkopf/HeartbeatSender.java index a7cb3ba..cb52c67 100644 --- a/Backend/schafkopf-client/src/main/java/org/schafkopf/HeartbeatSender.java +++ b/Backend/schafkopf-client/src/main/java/org/schafkopf/HeartbeatSender.java @@ -2,9 +2,13 @@ package org.schafkopf; import java.util.Timer; import java.util.TimerTask; +import org.schafkopf.SchafkopfMessage.SchafkopfBaseMessage; -/** Creates an Instance of the Backend Server. */ +/** + * Creates an Instance of the Backend Server. + */ public class HeartbeatSender { + private static final int HEARTBEAT_INTERVAL = 15000; // 1 minute private final DedicatedServerConnection client; @@ -13,13 +17,16 @@ public class HeartbeatSender { this.client = client; } - /** Creates an Instance of the Backend Server. */ + /** + * 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 + client.sendMessage( + new SchafkopfBaseMessage(SchafkopfMessage.SchafkopfMessageType.HEARTBEAT_SYN)); } }, HEARTBEAT_INTERVAL, HEARTBEAT_INTERVAL); } diff --git a/Backend/schafkopf-client/src/main/java/org/schafkopf/SchafkopfClient.java b/Backend/schafkopf-client/src/main/java/org/schafkopf/SchafkopfClient.java new file mode 100644 index 0000000..c505ec4 --- /dev/null +++ b/Backend/schafkopf-client/src/main/java/org/schafkopf/SchafkopfClient.java @@ -0,0 +1,81 @@ +package org.schafkopf; + +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import org.schafkopf.SchafkopfMessage.SchafkopfBaseMessage; +import org.schafkopf.SchafkopfMessage.SchafkopfMessageOrigin; +import org.schafkopf.SchafkopfMessage.SchafkopfMessageType; + +/** + * Class that represents one Frontend Connection. + */ +public class SchafkopfClient implements MessageListener { + + private BackendServer backendServer; + private DedicatedServerConnection dedicatedServerConnection; + + /** + * Class that represents one Frontend Connection. + */ + public SchafkopfClient() throws Exception { + + this.backendServer = new BackendServer("localhost", 8080, true, this); + + System.out.println("Client started."); + } + + public static void main(String[] args) throws Exception { + new SchafkopfClient(); + } + + @Override + public void receiveMessage(String jsonMessage) { + Gson gson = new Gson(); + JsonObject jsonObject = gson.fromJson(jsonMessage, JsonObject.class); + // Check if the origin is "frontend" + String origin = jsonObject.get("origin").getAsString(); + if (SchafkopfMessageOrigin.FRONTEND.toString().equals(origin)) { + JsonObject message = jsonObject.getAsJsonObject("message"); + JsonObject content = message.getAsJsonObject("content"); + String messageType = message.get("message_type").getAsString(); + if (SchafkopfMessageType.REQUEST_SERVER_CONNECTION.toString().equals(messageType)) { + dedicatedServerConnection = new DedicatedServerConnection( + content.get("serverAddress").getAsString(), + this); + } else if ("JOIN_GAME".equals(messageType)) { + dedicatedServerConnection.join(); + } else if ("START_DEDICATED_GAME".equals(messageType)) { + dedicatedServerConnection.start(); + } else if (SchafkopfMessageType.PLAYER_CARD.toString().equals(messageType)) { + dedicatedServerConnection.sendMessage( + new SchafkopfBaseMessage(SchafkopfMessageType.PLAYER_CARD, content)); + } + + System.out.println("Received message from frontend server: " + jsonMessage); + + } else if (SchafkopfMessageOrigin.DEDICATED_SERVER.toString().equals(origin)) { + + JsonObject message = jsonObject.getAsJsonObject("message"); + JsonObject content = message.getAsJsonObject("content"); + String messageType = message.get("message_type").getAsString(); + if (SchafkopfMessageType.GET_CARD_ONLINE_PLAYER.toString().equals(messageType)) { + System.out.println("Received get_card_online_player message from dedicated server."); + } else if (SchafkopfMessageType.GAME_STATE.toString().equals(messageType)) { + backendServer.sendMessage( + new SchafkopfBaseMessage(SchafkopfMessage.SchafkopfMessageType.GAME_STATE, content)); + } else if (SchafkopfMessageType.ONLINE_PLAYER_HAND.toString().equals(messageType)) { + backendServer.sendMessage( + new SchafkopfBaseMessage(SchafkopfMessage.SchafkopfMessageType.ONLINE_PLAYER_HAND, + content)); + } else if (SchafkopfMessageType.SERVER_CONNECTION_SUCCESSFUL.toString().equals(messageType)) { + System.out.println("Received server_connection_successful message from dedicated server."); + backendServer.sendMessage(new SchafkopfBaseMessage( + SchafkopfMessage.SchafkopfMessageType.SERVER_CONNECTION_SUCCESSFUL)); + } else if (SchafkopfMessageType.HEARTBEAT_ACK.toString().equals(messageType)) { + return; + } + System.out.println("Received message from dedicated server: " + jsonMessage); + } + } +} + diff --git a/Backend/schafkopf-client/src/main/java/org/schafkopf/cardreader/CardReader.java b/Backend/schafkopf-client/src/main/java/org/schafkopf/cardreader/CardReader.java index 1e7bdde..97cc6f6 100644 --- a/Backend/schafkopf-client/src/main/java/org/schafkopf/cardreader/CardReader.java +++ b/Backend/schafkopf-client/src/main/java/org/schafkopf/cardreader/CardReader.java @@ -1,13 +1,42 @@ package org.schafkopf.cardreader; -import org.schafkopf.BackendServer; +import java.util.concurrent.CountDownLatch; /** Class that represents one Card Reader. */ public abstract class CardReader { - protected static BackendServer server; + private CountDownLatch nfcLatch = new CountDownLatch(1); + private Boolean readingMode = false; + private String uidString = ""; - public CardReader(BackendServer server) { - this.server = server; + public CardReader() { + + } + + /** method to call to wait for NFC input. */ + public String waitForCardScan() throws InterruptedException { + this.readingMode = true; + nfcLatch.await(); + Thread.sleep(20); + this.readingMode = false; + nfcLatch = new CountDownLatch(1); + return this.uidString; + } + + /** + * checks uid of scanned card and do nothing if Server is not in reading mode. + * + * @param uidString uid to check. + */ + public void nfcGelesen(String uidString) { + if (this.uidString.equals(uidString)) { + return; + } + if (!this.readingMode) { + return; + } + + this.uidString = uidString; + nfcLatch.countDown(); } } 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 62f235a..9d20af9 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 @@ -16,10 +16,8 @@ public class UsbCardReader extends CardReader { /** * Creates an Instance of the KartenLeser. * - * @param server Backend Server to call methods on. */ - public UsbCardReader(BackendServer server) { - super(server); + public UsbCardReader() { new Thread(this::run).start(); } @@ -80,7 +78,7 @@ public class UsbCardReader extends CardReader { String data = new String(buffer, 0, bytesRead, "UTF-8").trim(); // Process the received data - server.nfcGelesen(data); + this.nfcGelesen(data); } // Optional: Add a delay to avoid consuming too much CPU 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 b543a94..555aef5 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,6 +1,7 @@ package org.schafkopf.player; import org.schafkopf.BackendServer; +import org.schafkopf.cardreader.CardReader; import org.schafkopf.karte.Karte; import org.schafkopf.karte.KartenListe; import org.schafkopf.karte.KartenUtil; @@ -11,10 +12,10 @@ import org.schafkopf.spielcontroller.SpielController; */ public class LocalPlayer extends Player { - private final BackendServer server; + private final CardReader cardReader; - public LocalPlayer(BackendServer server) { - this.server = server; + public LocalPlayer(CardReader cardReader) { + this.cardReader = cardReader; } @Override @@ -27,7 +28,7 @@ public class LocalPlayer extends Player { String uid = null; System.out.println("Starte Warten auf Karte"); try { - uid = server.waitForCardScan(); + uid = cardReader.waitForCardScan(); } catch (InterruptedException e) { throw new RuntimeException(e); } diff --git a/Backend/schafkopf-client/src/main/resources/web-content/assets/blatt_6.png b/Backend/schafkopf-client/src/main/resources/web-content/assets/blatt_6.png deleted file mode 100644 index 87dbdfe..0000000 Binary files a/Backend/schafkopf-client/src/main/resources/web-content/assets/blatt_6.png and /dev/null differ diff --git a/Backend/schafkopf-client/src/main/resources/web-content/assets/blatt_7.png b/Backend/schafkopf-client/src/main/resources/web-content/assets/blatt_7.png deleted file mode 100644 index f6f4b29..0000000 Binary files a/Backend/schafkopf-client/src/main/resources/web-content/assets/blatt_7.png and /dev/null differ diff --git a/Backend/schafkopf-client/src/main/resources/web-content/assets/blatt_8.png b/Backend/schafkopf-client/src/main/resources/web-content/assets/blatt_8.png deleted file mode 100644 index 4d5c938..0000000 Binary files a/Backend/schafkopf-client/src/main/resources/web-content/assets/blatt_8.png and /dev/null differ diff --git a/Backend/schafkopf-client/src/main/resources/web-content/assets/blatt_9.png b/Backend/schafkopf-client/src/main/resources/web-content/assets/blatt_9.png deleted file mode 100644 index 6ed0acf..0000000 Binary files a/Backend/schafkopf-client/src/main/resources/web-content/assets/blatt_9.png and /dev/null differ diff --git a/Backend/schafkopf-client/src/main/resources/web-content/assets/blatt_a.png b/Backend/schafkopf-client/src/main/resources/web-content/assets/blatt_a.png deleted file mode 100644 index eaed16b..0000000 Binary files a/Backend/schafkopf-client/src/main/resources/web-content/assets/blatt_a.png and /dev/null differ diff --git a/Backend/schafkopf-client/src/main/resources/web-content/assets/blatt_k.png b/Backend/schafkopf-client/src/main/resources/web-content/assets/blatt_k.png deleted file mode 100644 index 23008a3..0000000 Binary files a/Backend/schafkopf-client/src/main/resources/web-content/assets/blatt_k.png and /dev/null differ diff --git a/Backend/schafkopf-client/src/main/resources/web-content/assets/blatt_o.png b/Backend/schafkopf-client/src/main/resources/web-content/assets/blatt_o.png deleted file mode 100644 index eba2296..0000000 Binary files a/Backend/schafkopf-client/src/main/resources/web-content/assets/blatt_o.png and /dev/null differ diff --git a/Backend/schafkopf-client/src/main/resources/web-content/assets/blatt_u.png b/Backend/schafkopf-client/src/main/resources/web-content/assets/blatt_u.png deleted file mode 100644 index 17b9d91..0000000 Binary files a/Backend/schafkopf-client/src/main/resources/web-content/assets/blatt_u.png and /dev/null differ diff --git a/Backend/schafkopf-client/src/main/resources/web-content/assets/blatt_x.png b/Backend/schafkopf-client/src/main/resources/web-content/assets/blatt_x.png deleted file mode 100644 index c356615..0000000 Binary files a/Backend/schafkopf-client/src/main/resources/web-content/assets/blatt_x.png and /dev/null differ diff --git a/Backend/schafkopf-client/src/main/resources/web-content/assets/card_back.png b/Backend/schafkopf-client/src/main/resources/web-content/assets/card_back.png deleted file mode 100644 index a98948b..0000000 Binary files a/Backend/schafkopf-client/src/main/resources/web-content/assets/card_back.png and /dev/null differ diff --git a/Backend/schafkopf-client/src/main/resources/web-content/assets/eichel_6.png b/Backend/schafkopf-client/src/main/resources/web-content/assets/eichel_6.png deleted file mode 100644 index 01a5ef1..0000000 Binary files a/Backend/schafkopf-client/src/main/resources/web-content/assets/eichel_6.png and /dev/null differ diff --git a/Backend/schafkopf-client/src/main/resources/web-content/assets/eichel_7.png b/Backend/schafkopf-client/src/main/resources/web-content/assets/eichel_7.png deleted file mode 100644 index 3d9635b..0000000 Binary files a/Backend/schafkopf-client/src/main/resources/web-content/assets/eichel_7.png and /dev/null differ diff --git a/Backend/schafkopf-client/src/main/resources/web-content/assets/eichel_8.png b/Backend/schafkopf-client/src/main/resources/web-content/assets/eichel_8.png deleted file mode 100644 index 4b6ba4f..0000000 Binary files a/Backend/schafkopf-client/src/main/resources/web-content/assets/eichel_8.png and /dev/null differ diff --git a/Backend/schafkopf-client/src/main/resources/web-content/assets/eichel_9.png b/Backend/schafkopf-client/src/main/resources/web-content/assets/eichel_9.png deleted file mode 100644 index 791f03c..0000000 Binary files a/Backend/schafkopf-client/src/main/resources/web-content/assets/eichel_9.png and /dev/null differ diff --git a/Backend/schafkopf-client/src/main/resources/web-content/assets/eichel_a.png b/Backend/schafkopf-client/src/main/resources/web-content/assets/eichel_a.png deleted file mode 100644 index 9045e5b..0000000 Binary files a/Backend/schafkopf-client/src/main/resources/web-content/assets/eichel_a.png and /dev/null differ diff --git a/Backend/schafkopf-client/src/main/resources/web-content/assets/eichel_k.png b/Backend/schafkopf-client/src/main/resources/web-content/assets/eichel_k.png deleted file mode 100644 index 1f1b90c..0000000 Binary files a/Backend/schafkopf-client/src/main/resources/web-content/assets/eichel_k.png and /dev/null differ diff --git a/Backend/schafkopf-client/src/main/resources/web-content/assets/eichel_o.png b/Backend/schafkopf-client/src/main/resources/web-content/assets/eichel_o.png deleted file mode 100644 index 198db82..0000000 Binary files a/Backend/schafkopf-client/src/main/resources/web-content/assets/eichel_o.png and /dev/null differ diff --git a/Backend/schafkopf-client/src/main/resources/web-content/assets/eichel_u.png b/Backend/schafkopf-client/src/main/resources/web-content/assets/eichel_u.png deleted file mode 100644 index 77a2bb9..0000000 Binary files a/Backend/schafkopf-client/src/main/resources/web-content/assets/eichel_u.png and /dev/null differ diff --git a/Backend/schafkopf-client/src/main/resources/web-content/assets/eichel_x.png b/Backend/schafkopf-client/src/main/resources/web-content/assets/eichel_x.png deleted file mode 100644 index 8566280..0000000 Binary files a/Backend/schafkopf-client/src/main/resources/web-content/assets/eichel_x.png and /dev/null differ diff --git a/Backend/schafkopf-client/src/main/resources/web-content/assets/herz_6.png b/Backend/schafkopf-client/src/main/resources/web-content/assets/herz_6.png deleted file mode 100644 index 3e6135a..0000000 Binary files a/Backend/schafkopf-client/src/main/resources/web-content/assets/herz_6.png and /dev/null differ diff --git a/Backend/schafkopf-client/src/main/resources/web-content/assets/herz_7.png b/Backend/schafkopf-client/src/main/resources/web-content/assets/herz_7.png deleted file mode 100644 index 5d6b0bb..0000000 Binary files a/Backend/schafkopf-client/src/main/resources/web-content/assets/herz_7.png and /dev/null differ diff --git a/Backend/schafkopf-client/src/main/resources/web-content/assets/herz_8.png b/Backend/schafkopf-client/src/main/resources/web-content/assets/herz_8.png deleted file mode 100644 index c564577..0000000 Binary files a/Backend/schafkopf-client/src/main/resources/web-content/assets/herz_8.png and /dev/null differ diff --git a/Backend/schafkopf-client/src/main/resources/web-content/assets/herz_9.png b/Backend/schafkopf-client/src/main/resources/web-content/assets/herz_9.png deleted file mode 100644 index 35d955a..0000000 Binary files a/Backend/schafkopf-client/src/main/resources/web-content/assets/herz_9.png and /dev/null differ diff --git a/Backend/schafkopf-client/src/main/resources/web-content/assets/herz_a.png b/Backend/schafkopf-client/src/main/resources/web-content/assets/herz_a.png deleted file mode 100644 index 14f50da..0000000 Binary files a/Backend/schafkopf-client/src/main/resources/web-content/assets/herz_a.png and /dev/null differ diff --git a/Backend/schafkopf-client/src/main/resources/web-content/assets/herz_k.png b/Backend/schafkopf-client/src/main/resources/web-content/assets/herz_k.png deleted file mode 100644 index d0a4191..0000000 Binary files a/Backend/schafkopf-client/src/main/resources/web-content/assets/herz_k.png and /dev/null differ diff --git a/Backend/schafkopf-client/src/main/resources/web-content/assets/herz_o.png b/Backend/schafkopf-client/src/main/resources/web-content/assets/herz_o.png deleted file mode 100644 index 49e6358..0000000 Binary files a/Backend/schafkopf-client/src/main/resources/web-content/assets/herz_o.png and /dev/null differ diff --git a/Backend/schafkopf-client/src/main/resources/web-content/assets/herz_u.png b/Backend/schafkopf-client/src/main/resources/web-content/assets/herz_u.png deleted file mode 100644 index 6af0b88..0000000 Binary files a/Backend/schafkopf-client/src/main/resources/web-content/assets/herz_u.png and /dev/null differ diff --git a/Backend/schafkopf-client/src/main/resources/web-content/assets/herz_x.png b/Backend/schafkopf-client/src/main/resources/web-content/assets/herz_x.png deleted file mode 100644 index 2cce721..0000000 Binary files a/Backend/schafkopf-client/src/main/resources/web-content/assets/herz_x.png and /dev/null differ diff --git a/Backend/schafkopf-client/src/main/resources/web-content/assets/index-8PJRhba5.css b/Backend/schafkopf-client/src/main/resources/web-content/assets/index-8PJRhba5.css deleted file mode 100644 index 23f2aca..0000000 --- a/Backend/schafkopf-client/src/main/resources/web-content/assets/index-8PJRhba5.css +++ /dev/null @@ -1 +0,0 @@ -*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }.absolute{position:absolute}.\!left-1\/2{left:50%!important}.\!top-1\/2{top:50%!important}.bottom-0{bottom:0}.left-0{left:0}.top-1\/2{top:50%}.top-52{top:13rem}.top-64{top:16rem}.\!z-10{z-index:10!important}.mx-auto{margin-left:auto;margin-right:auto}.flex{display:flex}.grid{display:grid}.h-\[48rem\]{height:48rem}.h-full{height:100%}.w-fit{width:-moz-fit-content;width:fit-content}.w-full{width:100%}.-translate-y-1\/2{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.\!scale-105{--tw-scale-x: 1.05 !important;--tw-scale-y: 1.05 !important;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))!important}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.flex-row{flex-direction:row}.place-content-center{place-content:center}.justify-center{justify-content:center}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.overflow-hidden{overflow:hidden}.rounded-2xl,.rounded-\[1rem\]{border-radius:1rem}.rounded-\[2rem\]{border-radius:2rem}.rounded-xl{border-radius:.75rem}.bg-zinc-800{--tw-bg-opacity: 1;background-color:rgb(39 39 42 / var(--tw-bg-opacity))}.text-center{text-align:center}.text-6xl{font-size:3.75rem;line-height:1}.font-bold{font-weight:700}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.v-button{border-radius:9999px;--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity));padding:.5rem 1rem;font-weight:700;--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-duration:.3s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.v-button:hover{--tw-bg-opacity: 1;background-color:rgb(29 78 216 / var(--tw-bg-opacity))}.card.xl{height:48rem}.card.xl img{border-radius:2rem}.card.md{height:24rem}.card.md img{border-radius:1rem}.card.sm{height:16rem}.card.sm img{border-radius:.75rem}.card0,.card1{z-index:1;top:50%;left:50%;transform:translate(-50%,-50%)}.card2{z-index:2;top:calc(50% + 2.4rem);left:calc(50% - 7.2rem);transform:rotate(50deg) translate(-50%,-50%)}.card3{z-index:3;top:calc(50% - 3rem);left:calc(50% + 8.4rem);transform:rotate(-30deg) translate(-50%,-50%)}.card4{z-index:4;top:calc(50% - 9.6rem);left:calc(50% + 8.4rem);transform:rotate(-60deg) translate(-50%,-50%)} diff --git a/Backend/schafkopf-client/src/main/resources/web-content/assets/schell_6.png b/Backend/schafkopf-client/src/main/resources/web-content/assets/schell_6.png deleted file mode 100644 index b47d3d7..0000000 Binary files a/Backend/schafkopf-client/src/main/resources/web-content/assets/schell_6.png and /dev/null differ diff --git a/Backend/schafkopf-client/src/main/resources/web-content/assets/schell_7.png b/Backend/schafkopf-client/src/main/resources/web-content/assets/schell_7.png deleted file mode 100644 index 464ba51..0000000 Binary files a/Backend/schafkopf-client/src/main/resources/web-content/assets/schell_7.png and /dev/null differ diff --git a/Backend/schafkopf-client/src/main/resources/web-content/assets/schell_8.png b/Backend/schafkopf-client/src/main/resources/web-content/assets/schell_8.png deleted file mode 100644 index ef2da1a..0000000 Binary files a/Backend/schafkopf-client/src/main/resources/web-content/assets/schell_8.png and /dev/null differ diff --git a/Backend/schafkopf-client/src/main/resources/web-content/assets/schell_9.png b/Backend/schafkopf-client/src/main/resources/web-content/assets/schell_9.png deleted file mode 100644 index 09040e6..0000000 Binary files a/Backend/schafkopf-client/src/main/resources/web-content/assets/schell_9.png and /dev/null differ diff --git a/Backend/schafkopf-client/src/main/resources/web-content/assets/schell_a.png b/Backend/schafkopf-client/src/main/resources/web-content/assets/schell_a.png deleted file mode 100644 index af9f7fb..0000000 Binary files a/Backend/schafkopf-client/src/main/resources/web-content/assets/schell_a.png and /dev/null differ diff --git a/Backend/schafkopf-client/src/main/resources/web-content/assets/schell_k.png b/Backend/schafkopf-client/src/main/resources/web-content/assets/schell_k.png deleted file mode 100644 index e3b65cc..0000000 Binary files a/Backend/schafkopf-client/src/main/resources/web-content/assets/schell_k.png and /dev/null differ diff --git a/Backend/schafkopf-client/src/main/resources/web-content/assets/schell_o.png b/Backend/schafkopf-client/src/main/resources/web-content/assets/schell_o.png deleted file mode 100644 index b4fdfe3..0000000 Binary files a/Backend/schafkopf-client/src/main/resources/web-content/assets/schell_o.png and /dev/null differ diff --git a/Backend/schafkopf-client/src/main/resources/web-content/assets/schell_u.png b/Backend/schafkopf-client/src/main/resources/web-content/assets/schell_u.png deleted file mode 100644 index 8471a8b..0000000 Binary files a/Backend/schafkopf-client/src/main/resources/web-content/assets/schell_u.png and /dev/null differ diff --git a/Backend/schafkopf-client/src/main/resources/web-content/assets/schell_x.png b/Backend/schafkopf-client/src/main/resources/web-content/assets/schell_x.png deleted file mode 100644 index d5d0cc9..0000000 Binary files a/Backend/schafkopf-client/src/main/resources/web-content/assets/schell_x.png and /dev/null differ diff --git a/Backend/schafkopf-client/src/main/resources/web-content/index.html b/Backend/schafkopf-client/src/main/resources/web-content/index.html deleted file mode 100644 index 3739786..0000000 --- a/Backend/schafkopf-client/src/main/resources/web-content/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - Vite + Vue + TS - - - - -
- - diff --git a/Backend/schafkopf-server/src/main/java/org/schafkopf/ClientConnection.java b/Backend/schafkopf-server/src/main/java/org/schafkopf/ClientConnection.java deleted file mode 100644 index 0038b52..0000000 --- a/Backend/schafkopf-server/src/main/java/org/schafkopf/ClientConnection.java +++ /dev/null @@ -1,75 +0,0 @@ -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 index d86fe8e..3dde365 100644 --- a/Backend/schafkopf-server/src/main/java/org/schafkopf/DedicatedServer.java +++ b/Backend/schafkopf-server/src/main/java/org/schafkopf/DedicatedServer.java @@ -1,7 +1,11 @@ package org.schafkopf; import jakarta.servlet.DispatcherType; +import java.net.DatagramSocket; +import java.net.InetAddress; import java.net.InetSocketAddress; +import java.net.SocketException; +import java.net.UnknownHostException; import java.time.Duration; import java.util.ArrayList; import java.util.EnumSet; @@ -13,27 +17,50 @@ 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. */ +/** + * 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 clientConnections = new ArrayList<>(); private final List gameSessions = new ArrayList<>(); - /** Creates an Instance of the Backend Server. */ + private GameSession currentGameSession; + + /** + * Creates an Instance of the Backend Server. + */ public DedicatedServer() { server = new Server(); - InetSocketAddress address = new InetSocketAddress("localhost", 8085); + InetAddress address; + + try (final DatagramSocket socket = new DatagramSocket()) { + socket.connect(InetAddress.getByName("8.8.8.8"), 10002); + address = socket.getLocalAddress(); + } catch (UnknownHostException e) { + throw new RuntimeException(e); + } catch (SocketException e) { + throw new RuntimeException(e); + } + + InetSocketAddress socketAddress = new InetSocketAddress(address.getHostAddress(), 8085); + System.out.println( + "Server started at: " + socketAddress.getAddress() + ":" + socketAddress.getPort()); connector = new ServerConnector(server); - connector.setHost(address.getHostName()); - connector.setPort(address.getPort()); + connector.setHost(socketAddress.getHostName()); + connector.setPort(socketAddress.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); + + // Add the health check servlet to the servlet context + context.addServlet(HealthCheckServlet.class, "/health"); context.setContextPath("/"); server.setHandler(context); // Configure CORS settings @@ -48,9 +75,13 @@ public class DedicatedServer { // Add websockets wsContainer.addMapping("/*", new FrontendEndpointCreator(this)); }); + + currentGameSession = new GameSession(); } - /** The main entrypoint of the Application. */ + /** + * The main entrypoint of the Application. + */ public static void main(String[] args) throws Exception { DedicatedServer server = new DedicatedServer(); server.start(); @@ -79,16 +110,23 @@ public class DedicatedServer { server.join(); } - public void addFrontendEndpoint(ClientConnection endpoint) { + public void addFrontendEndpoint(SchafkopfClientConnection endpoint) { clientConnections.add(endpoint); } - public void removeFrontendEndpoint(ClientConnection endpoint) { + public void removeFrontendEndpoint(SchafkopfClientConnection endpoint) { clientConnections.remove(endpoint); } - public void addGameSession(GameSession gameSession) { gameSessions.add(gameSession); } + + public List getGameSessions() { + return gameSessions; + } + + public GameSession getCurrentGameSession() { + return currentGameSession; + } } diff --git a/Backend/schafkopf-server/src/main/java/org/schafkopf/FrontendEndpointCreator.java b/Backend/schafkopf-server/src/main/java/org/schafkopf/FrontendEndpointCreator.java index fe498b7..061a7ff 100644 --- a/Backend/schafkopf-server/src/main/java/org/schafkopf/FrontendEndpointCreator.java +++ b/Backend/schafkopf-server/src/main/java/org/schafkopf/FrontendEndpointCreator.java @@ -8,6 +8,7 @@ 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) { @@ -18,6 +19,6 @@ public class FrontendEndpointCreator implements JettyWebSocketCreator { public Object createWebSocket( JettyServerUpgradeRequest jettyServerUpgradeRequest, JettyServerUpgradeResponse jettyServerUpgradeResponse) { - return new ClientConnection(this.dedicatedServer); + return new SchafkopfClientConnection(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 index 5e38e25..dc1d440 100644 --- a/Backend/schafkopf-server/src/main/java/org/schafkopf/GameSession.java +++ b/Backend/schafkopf-server/src/main/java/org/schafkopf/GameSession.java @@ -1,19 +1,86 @@ package org.schafkopf; -/** The main entrypoint of the Application. */ -public class GameSession { +import java.util.ArrayList; +import java.util.List; +import org.schafkopf.SchafkopfException.NotEnoughPlayersException; +import org.schafkopf.SchafkopfMessage.SchafkopfBaseMessage; +import org.schafkopf.player.BotPlayer; +import org.schafkopf.player.OnlinePlayer; +import org.schafkopf.player.Player; + +/** + * The main entrypoint of the Application. + */ +public class GameSession implements MessageSender { private Schafkopf schafkopf; + private List player; - /** The main entrypoint of the Application. */ - public GameSession(Schafkopf schafkopf) { - this.schafkopf = schafkopf; - System.out.println("new GameSession created."); - startGame(); + private List clients; + + private Thread spielThread; + + /** + * The main entrypoint of the Application. + */ + public GameSession() { + player = new ArrayList<>(); + clients = new ArrayList<>(); } - private void startGame() { - schafkopf.startGame(); + /** + * Class that represents one Frontend Connection. + */ + public void addPlayer(SchafkopfClientConnection client) { + if (this.player.size() >= 4) { + throw new RuntimeException("Game is full"); + } + System.out.println("Adding player to game: " + client); + clients.add(client); + + OnlinePlayer onlinePlayer = new OnlinePlayer(client); + + this.player.add(onlinePlayer); + + client.setOnlinePlayer(onlinePlayer); } + public Schafkopf getSchafkopf() { + return schafkopf; + } + + void startGame() throws NotEnoughPlayersException { + int playerCount = this.player.size(); + System.out.println("Starting game with " + playerCount + " players"); + if (playerCount < 4) { + for (int i = 0; i < 4 - playerCount; i++) { + this.player.add(new BotPlayer()); + } + } + System.out.println("Starting game with, now: " + this.player.size() + " players"); + + spielThread = new Thread(() -> { + try { + schafkopf = new Schafkopf(this.player.toArray(new Player[0]), this); + schafkopf.startGame(); + } catch (NotEnoughPlayersException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + }); + + spielThread.start(); + + // schafkopf = new Schafkopf(this.player.toArray(new Player[0]), this); + + } + + @Override + public void sendMessage(SchafkopfBaseMessage message) { + System.out.println("Sending message to Client: " + message); + for (SchafkopfClientConnection client : clients) { + client.sendMessage(message); + } + } } diff --git a/Backend/schafkopf-server/src/main/java/org/schafkopf/HealthCheckServlet.java b/Backend/schafkopf-server/src/main/java/org/schafkopf/HealthCheckServlet.java new file mode 100644 index 0000000..828af19 --- /dev/null +++ b/Backend/schafkopf-server/src/main/java/org/schafkopf/HealthCheckServlet.java @@ -0,0 +1,23 @@ +package org.schafkopf; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * Class that represents one Frontend Connection. + */ +public class HealthCheckServlet extends HttpServlet { + + /** + * Class that represents one Frontend Connection. + */ + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + resp.setContentType("text/plain"); + resp.getWriter().println("Backend server is up and running!"); + } +} diff --git a/Backend/schafkopf-server/src/main/java/org/schafkopf/SchafkopfClientConnection.java b/Backend/schafkopf-server/src/main/java/org/schafkopf/SchafkopfClientConnection.java new file mode 100644 index 0000000..484656f --- /dev/null +++ b/Backend/schafkopf-server/src/main/java/org/schafkopf/SchafkopfClientConnection.java @@ -0,0 +1,134 @@ +package org.schafkopf; + +import com.google.gson.Gson; +import com.google.gson.JsonObject; +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.SchafkopfException.NotEnoughPlayersException; +import org.schafkopf.SchafkopfMessage.SchafkopfBaseMessage; +import org.schafkopf.SchafkopfMessage.SchafkopfMessageOrigin; +import org.schafkopf.SchafkopfMessage.SchafkopfMessageType; +import org.schafkopf.karte.Karte; +import org.schafkopf.player.OnlinePlayer; + +/** + * Class that represents one Frontend Connection. + */ +public class SchafkopfClientConnection extends WebSocketAdapter implements MessageSender { + + private final CountDownLatch connectionLatch; + private final CountDownLatch closureLatch = new CountDownLatch(1); + private DedicatedServer dedicatedServer; + + private GameSession gameSession; + + private Session session; + + private OnlinePlayer onlinePlayer; + + /** + * Class that represents one Frontend Connection. + */ + public SchafkopfClientConnection(DedicatedServer dedicatedServer) { + this.dedicatedServer = dedicatedServer; + this.connectionLatch = new CountDownLatch(1); + 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); + + connectionLatch.countDown(); + dedicatedServer.addFrontendEndpoint(this); + sendMessage(new SchafkopfBaseMessage(SchafkopfMessageType.SERVER_CONNECTION_SUCCESSFUL)); + } + + @Override + public void onWebSocketText(String jsonMessage) { + super.onWebSocketText(jsonMessage); + Gson gson = new Gson(); + JsonObject jsonObject = gson.fromJson(jsonMessage, JsonObject.class); + // Check if the origin is "frontend" + String origin = jsonObject.get("origin").getAsString(); + if (SchafkopfMessageOrigin.BACKEND.toString().equals(origin)) { + JsonObject message = jsonObject.getAsJsonObject("message"); + JsonObject content = message.getAsJsonObject("content"); + String messageType = message.get("message_type").getAsString(); + if ("HEARTBEAT_SYN".equals(messageType)) { + + sendMessage(new SchafkopfBaseMessage(SchafkopfMessageType.HEARTBEAT_ACK)); + return; + } else if (SchafkopfMessageType.JOIN_GAME.toString().equals(messageType)) { + + gameSession = dedicatedServer.getCurrentGameSession(); + dedicatedServer.getCurrentGameSession().addPlayer(this); + + } else if ("START_GAME".equals(messageType)) { + + System.out.println("Received START_GAME message from " + session.getRemoteAddress() + "."); + try { + dedicatedServer.getCurrentGameSession().startGame(); + } catch (NotEnoughPlayersException e) { + + sendMessage(new SchafkopfBaseMessage(SchafkopfMessageType.UNKNOWN_ERROR, + "Not enough players to start the game.")); + + } + + } else if (SchafkopfMessageType.PLAYER_CARD.toString().equals(messageType)) { + + onlinePlayer.receiveCard(Karte.valueOf(content.get("card").getAsString())); + + } else if ("list_online_games".equals(messageType)) { + System.out.println( + "Received list_online_games message from " + session.getRemoteAddress() + "."); + } + + System.out.println( + "Received message from Client " + session.getRemoteAddress() + " " + jsonMessage); + } + } + + + public void setOnlinePlayer(OnlinePlayer onlinePlayer) { + this.onlinePlayer = onlinePlayer; + } + + @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(SchafkopfBaseMessage message) { + SchafkopfMessage schafkopfMessage = new SchafkopfMessage( + SchafkopfMessageOrigin.DEDICATED_SERVER, + message); + System.out.println("Sending message to Client: " + schafkopfMessage.getMessageAsString()); + try { + getRemote().sendString(schafkopfMessage.getMessageAsString()); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/Backend/schafkopf-shared/src/main/java/org/schafkopf/GameState.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/GameState.java index d7d9e52..0ffc1a4 100644 --- a/Backend/schafkopf-shared/src/main/java/org/schafkopf/GameState.java +++ b/Backend/schafkopf-shared/src/main/java/org/schafkopf/GameState.java @@ -5,14 +5,18 @@ import com.google.gson.JsonObject; import org.schafkopf.karte.Karte; import org.schafkopf.karte.KartenFarbe; -/** GameState. */ +/** + * GameState. + */ public class GameState { public GamePhase getGamePhase() { return this.gamePhase; } - /** GamePhase. */ + /** + * GamePhase. + */ public enum GamePhase { CHOOSE_GAME("Spiel muss gewählt werden"), @@ -53,7 +57,9 @@ public class GameState { this.currentPlayer = player; } - /** GameState. */ + /** + * GameState. + */ public GameState(GamePhase phase, Integer player, Karte card, KartenFarbe color, boolean trumpf) { this.gamePhase = phase; this.currentPlayer = player; @@ -62,19 +68,34 @@ public class GameState { this.trumpf = trumpf; } - /** GameState. */ + /** + * GameState. + */ public GameState(GamePhase phase, Integer player, Karte card) { this.gamePhase = phase; this.currentPlayer = player; this.card = card; } - /** GameState. */ + /** + * GameState. + */ public JsonObject getJson() { Gson gson = new Gson(); - JsonObject jsonObject = new JsonObject(); - jsonObject.add("gamestate", gson.toJsonTree(this)); + JsonObject gameStateObject = new JsonObject(); - return jsonObject; + if (this.currentPlayer != null) { + gameStateObject.addProperty("currentPlayer", this.currentPlayer); + } + if (this.card != null) { + gameStateObject.add("card", gson.toJsonTree(this.card)); + } + gameStateObject.addProperty("gamePhase", this.gamePhase.name()); + gameStateObject.addProperty("trumpf", this.trumpf); + if (this.color != null) { + gameStateObject.addProperty("color", this.color.name()); + } + + return gameStateObject; } } diff --git a/Backend/schafkopf-shared/src/main/java/org/schafkopf/MessageListener.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/MessageListener.java new file mode 100644 index 0000000..03497b5 --- /dev/null +++ b/Backend/schafkopf-shared/src/main/java/org/schafkopf/MessageListener.java @@ -0,0 +1,9 @@ +package org.schafkopf; + +/** + * Class that represents one Frontend Connection. + */ +public interface MessageListener { + + void receiveMessage(String message); +} diff --git a/Backend/schafkopf-shared/src/main/java/org/schafkopf/MessageSender.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/MessageSender.java index a40037a..a8425af 100644 --- a/Backend/schafkopf-shared/src/main/java/org/schafkopf/MessageSender.java +++ b/Backend/schafkopf-shared/src/main/java/org/schafkopf/MessageSender.java @@ -1,6 +1,11 @@ package org.schafkopf; -/** The main entrypoint of the Application. */ +import org.schafkopf.SchafkopfMessage.SchafkopfBaseMessage; + +/** + * The main entrypoint of the Application. + */ public interface MessageSender { - void sendMessage(String message); + + void sendMessage(SchafkopfBaseMessage message); } diff --git a/Backend/schafkopf-shared/src/main/java/org/schafkopf/Schafkopf.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/Schafkopf.java index 15bac2f..25fbb88 100644 --- a/Backend/schafkopf-shared/src/main/java/org/schafkopf/Schafkopf.java +++ b/Backend/schafkopf-shared/src/main/java/org/schafkopf/Schafkopf.java @@ -1,25 +1,31 @@ package org.schafkopf; +import com.google.gson.Gson; +import com.google.gson.JsonObject; import org.schafkopf.GameState.GamePhase; +import org.schafkopf.SchafkopfException.NotEnoughPlayersException; +import org.schafkopf.SchafkopfMessage.SchafkopfBaseMessage; +import org.schafkopf.SchafkopfMessage.SchafkopfMessageType; import org.schafkopf.karte.Karte; import org.schafkopf.karte.KartenFarbe; import org.schafkopf.karte.KartenListe; import org.schafkopf.karte.KartenUtil; import org.schafkopf.player.BotPlayer; +import org.schafkopf.player.OnlinePlayer; import org.schafkopf.player.Player; -import org.schafkopf.spielcontroller.FarbGeierController; -import org.schafkopf.spielcontroller.FarbSoloController; -import org.schafkopf.spielcontroller.FarbWenzController; -import org.schafkopf.spielcontroller.GeierController; import org.schafkopf.spielcontroller.SauSpielController; import org.schafkopf.spielcontroller.SpielController; -import org.schafkopf.spielcontroller.WenzController; -/** The main class representing the Schafkopf game. */ +/** + * The main class representing the Schafkopf game. + */ public class Schafkopf { + private final MessageSender messageSender; - /** The game controller. This is the class that implements the game logic. */ + /** + * The game controller. This is the class that implements the game logic. + */ private SpielController spiel = new SauSpielController(0, KartenFarbe.EICHEL); private final Player[] player; @@ -32,8 +38,13 @@ public class Schafkopf { * * @param messageSender MessageSender */ - public Schafkopf(Player[] player, MessageSender messageSender) { + public Schafkopf(Player[] player, MessageSender messageSender) throws NotEnoughPlayersException { this.player = player; + + if (player.length < 4) { + throw new NotEnoughPlayersException(); + } + this.messageSender = messageSender; System.out.println("SchaffKopfGame erstellt"); } @@ -42,23 +53,24 @@ public class Schafkopf { return player; } - /** Set GameState to "started" and start Game Thread. */ - public void startGame() { + /** + * Set GameState to "started" and start Game Thread. + */ + public void startGame() throws InterruptedException { if (gameState.getGamePhase() != GamePhase.GAME_STOP) { System.out.println("Game already started!"); - messageSender.sendMessage("Game already started!"); + messageSender.sendMessage(new SchafkopfBaseMessage(SchafkopfMessageType.UNKNOWN_ERROR)); } else { gameState = new GameState(GamePhase.GAME_START); setAndSendGameState(gameState); System.out.println("Start Game"); KartenListe austeilen = KartenUtil.initializeSchafKopfCardDeck(); + austeilen.shuffle(); for (Player currentPlayer : player) { if (currentPlayer instanceof BotPlayer 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()); @@ -66,17 +78,35 @@ public class Schafkopf { } } - spielThread = new Thread(() -> new Spielablauf(this, spiel)); + for (Player currentPlayer : player) { + if (currentPlayer instanceof OnlinePlayer) { + Karte[] karten = new Karte[8]; + for (int i = 7; i >= 0; i--) { + karten[i] = austeilen.removeKarten(austeilen.getByIndex(i)); + } + Gson gson = new Gson(); + JsonObject messageObject = new JsonObject(); + messageObject.add("cards", gson.toJsonTree(karten)); - spielThread.start(); + messageSender.sendMessage( + new SchafkopfBaseMessage(SchafkopfMessageType.ONLINE_PLAYER_HAND, messageObject)); + } + } + + // spielThread = new Thread(() -> new Spielablauf(this, spiel)); + // + // spielThread.start(); + new Spielablauf(this, spiel); } } - /** Set GameState to "stopped" and interrupt Game Thread. */ + /** + * Set GameState to "stopped" and interrupt Game Thread. + */ public void stopGame() { if (gameState.getGamePhase() == GamePhase.GAME_STOP) { System.out.println("no active Game!"); - messageSender.sendMessage("no active Game!"); + messageSender.sendMessage(new SchafkopfBaseMessage(SchafkopfMessageType.UNKNOWN_ERROR)); } else { gameState = new GameState(GamePhase.GAME_STOP); setAndSendGameState(gameState); @@ -85,68 +115,13 @@ public class Schafkopf { spielThread.interrupt(); } - /** Set GameType. */ - public void setGame(String message) { - System.out.println("Set Game: " + message); - messageSender.sendMessage("Set Game: " + message); - switch (message) { - case "setgame:herzsolo": - this.spiel = new FarbSoloController(0, KartenFarbe.HERZ); - break; - case "setgame:blattsolo": - this.spiel = new FarbSoloController(0, KartenFarbe.BLATT); - break; - case "setgame:eichelsolo": - this.spiel = new FarbSoloController(0, KartenFarbe.EICHEL); - break; - case "setgame:schellsolo": - this.spiel = new FarbSoloController(0, KartenFarbe.SCHELL); - break; - - case "setgame:wenz": - this.spiel = new WenzController(0); - break; - case "setgame:geier": - this.spiel = new GeierController(0); - break; - - case "setgame:eichelwenz": - this.spiel = new FarbWenzController(0, KartenFarbe.EICHEL); - break; - case "setgame:herzwenz": - this.spiel = new FarbWenzController(0, KartenFarbe.HERZ); - break; - case "setgame:blattwenz": - this.spiel = new FarbWenzController(0, KartenFarbe.BLATT); - break; - case "setgame:schellwenz": - this.spiel = new FarbWenzController(0, KartenFarbe.SCHELL); - break; - - case "setgame:eichelgeier": - this.spiel = new FarbGeierController(0, KartenFarbe.EICHEL); - break; - case "setgame:herzgeier": - this.spiel = new FarbGeierController(0, KartenFarbe.HERZ); - break; - case "setgame:blattgeier": - this.spiel = new FarbGeierController(0, KartenFarbe.BLATT); - break; - case "setgame:schellgeier": - this.spiel = new FarbGeierController(0, KartenFarbe.SCHELL); - break; - - case "setgame:sauspiel": - this.spiel = new SauSpielController(0, KartenFarbe.EICHEL); - break; - default: - System.out.println("Ungültiges Spiel"); - } - } - + /** + * Class that represents one Frontend Connection. + */ public void setAndSendGameState(GameState gameState) { this.gameState = gameState; - this.messageSender.sendMessage(this.gameState.getJson().toString()); + this.messageSender.sendMessage( + new SchafkopfBaseMessage(SchafkopfMessageType.GAME_STATE, gameState.getJson())); } public GameState getGameState() { diff --git a/Backend/schafkopf-shared/src/main/java/org/schafkopf/SchafkopfException.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/SchafkopfException.java new file mode 100644 index 0000000..3413ae4 --- /dev/null +++ b/Backend/schafkopf-shared/src/main/java/org/schafkopf/SchafkopfException.java @@ -0,0 +1,37 @@ +package org.schafkopf; + +/** + * Class that represents one Frontend Connection. + */ +public class SchafkopfException extends Exception { + + private SchafkopfException(String message) { + super(message); + } + + /** + * Class that represents one Frontend Connection. + */ + public static class NotEnoughPlayersException extends SchafkopfException { + + public NotEnoughPlayersException() { + super("Not enough players to start the game"); + } + + // You can also include additional constructors or methods if needed + } + + /** + * Class that represents one Frontend Connection. + */ + public class InvalidMoveException extends SchafkopfException { + + // Constructor with a message + public InvalidMoveException(String message) { + super(message); + } + + // You can also include additional constructors or methods if needed + } +} + diff --git a/Backend/schafkopf-shared/src/main/java/org/schafkopf/SchafkopfMessage.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/SchafkopfMessage.java new file mode 100644 index 0000000..0859e19 --- /dev/null +++ b/Backend/schafkopf-shared/src/main/java/org/schafkopf/SchafkopfMessage.java @@ -0,0 +1,141 @@ +package org.schafkopf; + +import com.google.gson.JsonObject; + +/** + * Class that represents one Frontend Connection. + */ +public class SchafkopfMessage { + + /** + * Class that represents one Frontend Connection. + */ + public static class SchafkopfBaseMessage { + + private JsonObject message; + + public SchafkopfBaseMessage(SchafkopfMessageType messageType, String content) { + this.message = buildBaseMessage(messageType, content); + } + + public SchafkopfBaseMessage(SchafkopfMessageType messageType, JsonObject content) { + this.message = buildBaseMessage(messageType, content); + } + + public SchafkopfBaseMessage(SchafkopfMessageType messageType) { + this.message = buildBaseMessage(messageType); + } + + public JsonObject getBaseMessage() { + return message; + } + } + + JsonObject message; + + public SchafkopfMessage(SchafkopfMessageOrigin origin, SchafkopfBaseMessage baseMessage) { + this.message = buildWrapperMessage(origin, baseMessage.getBaseMessage()); + } + + /** + * Class that represents one Frontend Connection. + */ + public SchafkopfMessage(SchafkopfMessageOrigin origin, SchafkopfMessageType messageType) { + + JsonObject messageContentObject = new JsonObject(); + messageContentObject.add("content", buildBaseMessage(messageType)); + + this.message = buildWrapperMessage(origin, messageContentObject); + } + + /** + * Class that represents one Frontend Connection. + */ + private SchafkopfMessage(SchafkopfMessageOrigin origin, SchafkopfMessageType messageType, + JsonObject messageContent) { + + JsonObject messageContentObject = new JsonObject(); + messageContentObject.add("content", buildBaseMessage(messageType, messageContent)); + + this.message = buildWrapperMessage(origin, messageContentObject); + } + + /** + * Class that represents one Frontend Connection. + */ + public SchafkopfMessage(SchafkopfMessageOrigin origin, SchafkopfMessageType messageType, + String messageContent) { + + JsonObject messageContentObject = new JsonObject(); + messageContentObject.add("content", buildBaseMessage(messageType, messageContent)); + + this.message = buildWrapperMessage(origin, messageContentObject); + } + + private static JsonObject buildWrapperMessage(SchafkopfMessageOrigin origin, JsonObject message) { + JsonObject messageObject = new JsonObject(); + messageObject.addProperty("origin", origin.toString()); + messageObject.add("message", message); + + return messageObject; + } + + private static JsonObject buildBaseMessage(SchafkopfMessageType messageType, + String messageContent) { + JsonObject messageContentObject = new JsonObject(); + messageContentObject.addProperty("message_type", messageType.toString()); + messageContentObject.addProperty("content", messageContent); + + return messageContentObject; + } + + private static JsonObject buildBaseMessage(SchafkopfMessageType messageType, + JsonObject messageContent) { + JsonObject messageContentObject = new JsonObject(); + messageContentObject.addProperty("message_type", messageType.toString()); + messageContentObject.add("content", messageContent); + + return messageContentObject; + } + + private static JsonObject buildBaseMessage(SchafkopfMessageType messageType) { + JsonObject messageContentObject = new JsonObject(); + messageContentObject.addProperty("message_type", messageType.toString()); + + return messageContentObject; + } + + public JsonObject getMessage() { + return message; + } + + public String getMessageAsString() { + return message.toString(); + } + + /** + * Class that represents one Frontend Connection. + */ + public enum SchafkopfMessageType { + UNKNOWN_ERROR, + HEARTBEAT_SYN, + HEARTBEAT_ACK, + GET_CARD_ONLINE_PLAYER, + ONLINE_PLAYER_HAND, + GAME_STATE, + SERVER_CONNECTION_SUCCESSFUL, + REQUEST_SERVER_CONNECTION, + START_GAME, + JOIN_GAME, + PLAYER_CARD + } + + /** + * Class that represents one Frontend Connection. + */ + public enum SchafkopfMessageOrigin { + FRONTEND, + BACKEND, + DEDICATED_SERVER + } +} diff --git a/Backend/schafkopf-shared/src/main/java/org/schafkopf/Spielablauf.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/Spielablauf.java index 97cd13f..d16d6c7 100644 --- a/Backend/schafkopf-shared/src/main/java/org/schafkopf/Spielablauf.java +++ b/Backend/schafkopf-shared/src/main/java/org/schafkopf/Spielablauf.java @@ -8,7 +8,9 @@ import org.schafkopf.spielcontroller.SpielController; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -/** The main class that controlls the game flow. */ +/** + * The main class that controlls the game flow. + */ public class Spielablauf { private static final Logger logger = LoggerFactory.getLogger(Spielablauf.class); @@ -22,7 +24,7 @@ public class Spielablauf { private final Schafkopf schafkopf; - Spielablauf(Schafkopf schafkopf, SpielController spiel) { + Spielablauf(Schafkopf schafkopf, SpielController spiel) throws InterruptedException { this.schafkopf = schafkopf; this.spiel = spiel; this.players = schafkopf.getPlayer(); @@ -30,7 +32,7 @@ public class Spielablauf { playRound(); } - private void playRound() { + private void playRound() throws InterruptedException { int startingPlayer = 0; logger.info("Starte Stiche"); @@ -41,7 +43,7 @@ public class Spielablauf { schafkopf.stopGame(); } - private int playTrick(int startingPlayer) { + private int playTrick(int startingPlayer) throws InterruptedException { schafkopf.setAndSendGameState(new GameState(GamePhase.TRICK_START)); for (int i = 0; i < 4; i++) { diff --git a/Backend/schafkopf-shared/src/main/java/org/schafkopf/karte/KartenListe.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/karte/KartenListe.java index 2970f21..b90adaa 100644 --- a/Backend/schafkopf-shared/src/main/java/org/schafkopf/karte/KartenListe.java +++ b/Backend/schafkopf-shared/src/main/java/org/schafkopf/karte/KartenListe.java @@ -3,12 +3,14 @@ package org.schafkopf.karte; import com.google.gson.Gson; import com.google.gson.JsonObject; import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** * A Class that represents a list of Cards. */ public class KartenListe { + private List kartenListe; public KartenListe() { @@ -23,6 +25,10 @@ public class KartenListe { return this.kartenListe; } + public void shuffle() { + Collections.shuffle(this.kartenListe); + } + /** * A Class that represents a list of Cards. */ @@ -158,6 +164,24 @@ public class KartenListe { return result; } + /** + * Class that represents one Frontend Connection. + */ + public Karte getKarten(KartenSymbol symbol, KartenFarbe farbe) { + KartenListe result = new KartenListe(); + for (Karte karte : this.kartenListe) { + if (karte.getSymbol().equals(symbol)) { + result.addKarten(karte); + } + } + for (Karte karte : result.kartenListe) { + if (karte.getFarbe().equals(farbe)) { + result.addKarten(karte); + } + } + return result.getByIndex(0); + } + /** * A Class that represents a list of Cards. */ diff --git a/Backend/schafkopf-shared/src/main/java/org/schafkopf/player/OnlinePlayer.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/player/OnlinePlayer.java new file mode 100644 index 0000000..eaf963f --- /dev/null +++ b/Backend/schafkopf-shared/src/main/java/org/schafkopf/player/OnlinePlayer.java @@ -0,0 +1,46 @@ +package org.schafkopf.player; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import org.schafkopf.MessageSender; +import org.schafkopf.SchafkopfMessage.SchafkopfBaseMessage; +import org.schafkopf.SchafkopfMessage.SchafkopfMessageType; +import org.schafkopf.karte.Karte; +import org.schafkopf.karte.KartenListe; +import org.schafkopf.spielcontroller.SpielController; + +/** + * Player that plays in real life. + */ +public class OnlinePlayer extends Player { + + private final MessageSender messageSender; + private final BlockingQueue receivedCardQueue = new LinkedBlockingQueue<>(); + + public OnlinePlayer(MessageSender messageSender) { + this.messageSender = messageSender; + } + + @Override + public Karte play(SpielController spiel, KartenListe tischKarten, KartenListe gespielteKarten) + throws InterruptedException { + Karte spielKarte = null; + + // Send the message to request the card from the frontend + messageSender.sendMessage( + new SchafkopfBaseMessage(SchafkopfMessageType.GET_CARD_ONLINE_PLAYER)); + + spielKarte = receivedCardQueue.take(); + + System.out.println("Karte gespielt: " + spielKarte); + return spielKarte; + } + + /** + * Class that represents one Frontend Connection. + */ + public void receiveCard(Karte receivedCard) { + System.out.println("Received Card before Queue: " + receivedCard.getName()); + receivedCardQueue.add(receivedCard); + } +} diff --git a/Backend/schafkopf-shared/src/main/java/org/schafkopf/player/Player.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/player/Player.java index 2d921f0..1412b42 100644 --- a/Backend/schafkopf-shared/src/main/java/org/schafkopf/player/Player.java +++ b/Backend/schafkopf-shared/src/main/java/org/schafkopf/player/Player.java @@ -4,8 +4,12 @@ import org.schafkopf.karte.Karte; import org.schafkopf.karte.KartenListe; import org.schafkopf.spielcontroller.SpielController; -/** Class that represents one Player of the game. */ +/** + * Class that represents one Player of the game. + */ public abstract class Player { + public abstract Karte play( - SpielController spiel, KartenListe tischKarten, KartenListe gespielteKarten); + SpielController spiel, KartenListe tischKarten, KartenListe gespielteKarten) + throws InterruptedException; } diff --git a/Frontend/package-lock.json b/Frontend/package-lock.json index 98889e6..00d2b65 100644 --- a/Frontend/package-lock.json +++ b/Frontend/package-lock.json @@ -8,12 +8,16 @@ "name": "frontend", "version": "0.0.0", "dependencies": { - "vue": "^3.4.21" + "bootstrap-icons": "^1.11.3", + "ioc-service-container": "^1.6.1", + "vue": "^3.4.21", + "vue-router": "^4.3.2" }, "devDependencies": { "@typescript-eslint/parser": "^7.7.0", "@vitejs/plugin-vue": "^5.0.4", "autoprefixer": "^10.4.19", + "daisyui": "^4.10.2", "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-vue": "^9.25.0", @@ -1078,6 +1082,11 @@ "@vue/shared": "3.4.23" } }, + "node_modules/@vue/devtools-api": { + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.1.tgz", + "integrity": "sha512-LgPscpE3Vs0x96PzSSB4IGVSZXZBZHpfxs+ZA1d+VEPwHdOXowy/Y2CsvCAIFrf+ssVU1pD1jidj505EpUnfbA==" + }, "node_modules/@vue/language-core": { "version": "2.0.13", "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.0.13.tgz", @@ -1307,6 +1316,21 @@ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", "dev": true }, + "node_modules/bootstrap-icons": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.11.3.tgz", + "integrity": "sha512-+3lpHrCw/it2/7lBL15VR0HEumaBss0+f/Lb6ZvHISn1mlK83jjFpooTLsMWbIjJMDjDjOExMsTxnXSIT4k4ww==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ] + }, "node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -1518,6 +1542,16 @@ "node": ">= 8" } }, + "node_modules/css-selector-tokenizer": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.8.0.tgz", + "integrity": "sha512-Jd6Ig3/pe62/qe5SBPTN8h8LeUg/pT4lLgtavPf7updwwHpvFzxvOQBHYj2LZDMjUnBzgvIUSjRcf6oT5HzHFg==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "fastparse": "^1.1.2" + } + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -1535,6 +1569,34 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, + "node_modules/culori": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/culori/-/culori-3.3.0.tgz", + "integrity": "sha512-pHJg+jbuFsCjz9iclQBqyL3B2HLCBF71BwVNujUYEvCeQMvV97R59MNK3R2+jgJ3a1fcZgI9B3vYgz8lzr/BFQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/daisyui": { + "version": "4.10.2", + "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-4.10.2.tgz", + "integrity": "sha512-eCWS1W/JPyxW9IvlgW5m0R6rp9ZhRsBTW37rvEUthckkjsV04u8XipV519OoccSA46ixhSJa3q7XLI1WUFtRCA==", + "dev": true, + "dependencies": { + "css-selector-tokenizer": "^0.8", + "culori": "^3", + "picocolors": "^1", + "postcss-js": "^4" + }, + "engines": { + "node": ">=16.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/daisyui" + } + }, "node_modules/de-indent": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", @@ -1958,6 +2020,12 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "node_modules/fastparse": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", + "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", + "dev": true + }, "node_modules/fastq": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", @@ -2246,6 +2314,11 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, + "node_modules/ioc-service-container": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/ioc-service-container/-/ioc-service-container-1.6.1.tgz", + "integrity": "sha512-eXWKOjYubIyi7j+O3ZeJB401h9VO9TgTqkL8vXHscQfdY+VT9ng6N/bxKRjKbk9wJVIjhUOaFLPUQZZpEdW+XA==" + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -3674,6 +3747,20 @@ "eslint": ">=6.0.0" } }, + "node_modules/vue-router": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.3.2.tgz", + "integrity": "sha512-hKQJ1vDAZ5LVkKEnHhmm1f9pMiWIBNGF5AwU67PdH7TyXCj/a4hTccuUuYCAMgJK6rO/NVYtQIEN3yL8CECa7Q==", + "dependencies": { + "@vue/devtools-api": "^6.5.1" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "vue": "^3.2.0" + } + }, "node_modules/vue-template-compiler": { "version": "2.7.16", "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz", diff --git a/Frontend/package.json b/Frontend/package.json index bdb8f57..8a93d58 100644 --- a/Frontend/package.json +++ b/Frontend/package.json @@ -9,12 +9,16 @@ "preview": "vite preview" }, "dependencies": { - "vue": "^3.4.21" + "bootstrap-icons": "^1.11.3", + "ioc-service-container": "^1.6.1", + "vue": "^3.4.21", + "vue-router": "^4.3.2" }, "devDependencies": { "@typescript-eslint/parser": "^7.7.0", "@vitejs/plugin-vue": "^5.0.4", "autoprefixer": "^10.4.19", + "daisyui": "^4.10.2", "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-vue": "^9.25.0", diff --git a/Frontend/src/App.vue b/Frontend/src/App.vue index 064b083..66d6edb 100644 --- a/Frontend/src/App.vue +++ b/Frontend/src/App.vue @@ -1,235 +1,8 @@ diff --git a/Frontend/src/BackendMessage.ts b/Frontend/src/BackendMessage.ts index bcb773c..94e4572 100644 --- a/Frontend/src/BackendMessage.ts +++ b/Frontend/src/BackendMessage.ts @@ -71,6 +71,13 @@ export enum GamePhase { PLAYER_TRICK = "PLAYER_TRICK" } +export enum MessageType { + PLAYER_CARD = "PLAYER_CARD", + START_DEDICATED_GAME = "START_DEDICATED_GAME", + JOIN_GAME = "JOIN_GAME", + REQUEST_SERVER_CONNECTION = "REQUEST_SERVER_CONNECTION", +} + // Define the interface for an array of cards export interface CardArray { cards: Card[]; @@ -92,8 +99,13 @@ export interface GameState { export interface GameStateJson { - gamestate: GameState + gamestate: GameState, +} + +export interface EmptyMessage { + message_type: string; + content: GameStateJson | CardArray | CardObject; } // Define a union type for all possible message types -export type BackendMessage = CardObject | CardArray | GameStateJson; \ No newline at end of file +export type BackendMessage = EmptyMessage \ No newline at end of file diff --git a/Frontend/src/components/CardComponent.vue b/Frontend/src/components/CardComponent.vue index 1cec253..845ca43 100644 --- a/Frontend/src/components/CardComponent.vue +++ b/Frontend/src/components/CardComponent.vue @@ -21,7 +21,7 @@ watch(() => props.card, (newCard) => { diff --git a/Frontend/src/main.ts b/Frontend/src/main.ts index 2425c0f..ef929a6 100644 --- a/Frontend/src/main.ts +++ b/Frontend/src/main.ts @@ -1,5 +1,24 @@ -import { createApp } from 'vue' +import {createApp} from 'vue' import './style.css' import App from './App.vue' +import {createRouter, createWebHistory} from "vue-router"; +import {setupService} from "./services/DependencyInjection.ts"; -createApp(App).mount('#app') +const routes = [ + {path: '/', component: () => import('./pages/MainMenu.vue'),}, + {path: '/online', component: () => import('./pages/OnlineGameList.vue'),}, + {path: '/localgame', component: () => import('./pages/LocalGame.vue'),}, + {path: '/dedicatedgame', component: () => import('./pages/DedicatedGame.vue'),}, +] + +const router = createRouter({ + history: createWebHistory(), + routes, +}) + +const websocketIp = import.meta.env.VITE_APP_WEBSOCKET_IP; +setupService("ws://" + websocketIp + ":8080/schafkopf-events/"); + +const app = createApp(App) +app.use(router) +app.mount('#app') diff --git a/Frontend/src/pages/DedicatedGame.vue b/Frontend/src/pages/DedicatedGame.vue new file mode 100644 index 0000000..9738146 --- /dev/null +++ b/Frontend/src/pages/DedicatedGame.vue @@ -0,0 +1,181 @@ + + + diff --git a/Frontend/src/pages/LocalGame.vue b/Frontend/src/pages/LocalGame.vue new file mode 100644 index 0000000..4fa07f4 --- /dev/null +++ b/Frontend/src/pages/LocalGame.vue @@ -0,0 +1,222 @@ + + + diff --git a/Frontend/src/pages/MainMenu.vue b/Frontend/src/pages/MainMenu.vue new file mode 100644 index 0000000..435fdf2 --- /dev/null +++ b/Frontend/src/pages/MainMenu.vue @@ -0,0 +1,187 @@ + + + + + + \ No newline at end of file diff --git a/Frontend/src/pages/OnlineGameList.vue b/Frontend/src/pages/OnlineGameList.vue new file mode 100644 index 0000000..5453142 --- /dev/null +++ b/Frontend/src/pages/OnlineGameList.vue @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/Frontend/src/services/BackendConnection.ts b/Frontend/src/services/BackendConnection.ts new file mode 100644 index 0000000..8177eef --- /dev/null +++ b/Frontend/src/services/BackendConnection.ts @@ -0,0 +1,76 @@ +import {MessageType} from "../BackendMessage.ts"; + +interface JsonMessage { + origin: string; + message: any; // Adjust 'any' type as per your expected message structure +} + +export class BackendConnection { + + + private readonly webSocket: WebSocket; + private messageListeners: ((message: string) => void)[] = []; + private backendUri: string; + + + constructor(backendUri: string) { + this.backendUri = backendUri; + + this.webSocket = new WebSocket(backendUri); + + // Registering event listener for message reception + this.webSocket.addEventListener('message', this.handleMessage.bind(this)); + // Handle connection closed + this.webSocket.addEventListener("close", (event) => { + console.log("WebSocket connection closed:", event); + }); + + // Handle errors + this.webSocket.addEventListener("error", (event) => { + console.error("WebSocket error:", event); + }); + } + + public sendMessage(messageType: MessageType, message?: any): void { + let jsonMessage; + if (message === undefined) { + jsonMessage = { + origin: "FRONTEND", + message: { + message_type: messageType, + } + }; + } else { + jsonMessage = { + origin: "FRONTEND", + message: { + message_type: messageType, + content: message + } + }; + } + + console.log("Sending message:", jsonMessage); + this.webSocket.send(JSON.stringify(jsonMessage)); + } + + public getWebSocket(): WebSocket { + return this.webSocket; + } + + public addMessageListener(listener: (message: string) => void): void { + this.messageListeners.push(listener); + } + + public removeMessageListener(listener: (message: string) => void): void { + this.messageListeners = this.messageListeners.filter(l => l !== listener); + } + + private handleMessage(event: MessageEvent): void { + const message = event.data as string; + // Notify all registered message listeners + this.messageListeners.forEach(listener => { + listener(message); + }); + } +} \ No newline at end of file diff --git a/Frontend/src/services/DependencyInjection.ts b/Frontend/src/services/DependencyInjection.ts new file mode 100644 index 0000000..0bcae60 --- /dev/null +++ b/Frontend/src/services/DependencyInjection.ts @@ -0,0 +1,15 @@ +import {ServiceContainer} from "ioc-service-container"; +import {BackendConnection} from "./BackendConnection.ts"; + +type IoCTypes = { + BackendConnection: BackendConnection, +}; + +declare module 'ioc-service-container' { + export function scg(id: T): U; +} + +export function setupService(backendUri: string) { + const backendConnection = new BackendConnection(backendUri); + ServiceContainer.set('BackendConnection', () => backendConnection); +} \ No newline at end of file diff --git a/Frontend/tailwind.config.js b/Frontend/tailwind.config.js index ff48818..8741456 100644 --- a/Frontend/tailwind.config.js +++ b/Frontend/tailwind.config.js @@ -1,11 +1,11 @@ /** @type {import('tailwindcss').Config} */ export default { - content: [ - "./index.html", - "./src/**/*.{vue,js,ts,jsx,tsx}", - ], - theme: { - extend: {}, - }, - plugins: [], + content: [ + "./index.html", + "./src/**/*.{vue,js,ts,jsx,tsx}", + ], + theme: { + extend: {}, + }, + plugins: [require("daisyui")], } \ No newline at end of file