Message types and server (#44)
* message type and handling * deleted web-content and fixed bug * edited main page
@@ -135,5 +135,11 @@
|
||||
<groupId>io.github.cdimascio</groupId>
|
||||
<version>3.0.0</version>
|
||||
</dependency>
|
||||
<!-- Jackson for JSON serialization/deserialization -->
|
||||
<dependency>
|
||||
<groupId>org.json</groupId>
|
||||
<artifactId>json</artifactId>
|
||||
<version>20240303</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -41,7 +41,7 @@
|
||||
</descriptorRefs>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>org.schafkopf.BackendServer</mainClass>
|
||||
<mainClass>org.schafkopf.SchafkopfClient</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
|
||||
@@ -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<FrontendEndpoint> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 815 KiB |
|
Before Width: | Height: | Size: 859 KiB |
|
Before Width: | Height: | Size: 971 KiB |
|
Before Width: | Height: | Size: 954 KiB |
|
Before Width: | Height: | Size: 797 KiB |
|
Before Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 1.0 MiB |
|
Before Width: | Height: | Size: 1.0 MiB |
|
Before Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 603 KiB |
|
Before Width: | Height: | Size: 646 KiB |
|
Before Width: | Height: | Size: 730 KiB |
|
Before Width: | Height: | Size: 768 KiB |
|
Before Width: | Height: | Size: 905 KiB |
|
Before Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 1.0 MiB |
|
Before Width: | Height: | Size: 1008 KiB |
|
Before Width: | Height: | Size: 870 KiB |
|
Before Width: | Height: | Size: 412 KiB |
|
Before Width: | Height: | Size: 470 KiB |
|
Before Width: | Height: | Size: 498 KiB |
|
Before Width: | Height: | Size: 626 KiB |
|
Before Width: | Height: | Size: 957 KiB |
|
Before Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 1010 KiB |
|
Before Width: | Height: | Size: 959 KiB |
|
Before Width: | Height: | Size: 686 KiB |
|
Before Width: | Height: | Size: 600 KiB |
|
Before Width: | Height: | Size: 704 KiB |
|
Before Width: | Height: | Size: 741 KiB |
|
Before Width: | Height: | Size: 881 KiB |
|
Before Width: | Height: | Size: 1009 KiB |
|
Before Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 1.0 MiB |
|
Before Width: | Height: | Size: 1015 KiB |
|
Before Width: | Height: | Size: 958 KiB |
@@ -1,13 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>Vite + Vue + TS</title>
|
||||
<script type="module" crossorigin src="/assets/index-DZ_vcdmw.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-8PJRhba5.css">
|
||||
</head>
|
||||
<body class="bg-zinc-800">
|
||||
<div id="app"></div>
|
||||
|
||||
</body>
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<ClientConnection> clientConnections = new ArrayList<>();
|
||||
private final List<SchafkopfClientConnection> clientConnections = new ArrayList<>();
|
||||
|
||||
private final List<GameSession> 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<GameSession> getGameSessions() {
|
||||
return gameSessions;
|
||||
}
|
||||
|
||||
public GameSession getCurrentGameSession() {
|
||||
return currentGameSession;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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> player;
|
||||
|
||||
/** The main entrypoint of the Application. */
|
||||
public GameSession(Schafkopf schafkopf) {
|
||||
this.schafkopf = schafkopf;
|
||||
System.out.println("new GameSession created.");
|
||||
startGame();
|
||||
private List<SchafkopfClientConnection> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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!");
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package org.schafkopf;
|
||||
|
||||
/**
|
||||
* Class that represents one Frontend Connection.
|
||||
*/
|
||||
public interface MessageListener {
|
||||
|
||||
void receiveMessage(String message);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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++) {
|
||||
|
||||
@@ -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<Karte> 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.
|
||||
*/
|
||||
|
||||
@@ -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<Karte> 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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||