mirror of
https://github.com/Vale54321/schafkopf-bot.git
synced 2025-12-16 11:49:33 +01:00
init kotlin backend
This commit is contained in:
38
Backend/schafkopf-client/.gitignore
vendored
Normal file
38
Backend/schafkopf-client/.gitignore
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
.gradle
|
||||
build/
|
||||
!gradle/wrapper/gradle-wrapper.jar
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
bin/
|
||||
!**/src/main/**/bin/
|
||||
!**/src/test/**/bin/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
out/
|
||||
!**/src/main/**/out/
|
||||
!**/src/test/**/out/
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
src/main/resources/web-content
|
||||
@@ -1,60 +1,144 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.schafkopf</groupId>
|
||||
<artifactId>schafkopf-backend-java</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>de.heiserer</groupId>
|
||||
<artifactId>schafkopf-client</artifactId>
|
||||
<version>0.0.1</version>
|
||||
<name>schafkopf-client</name>
|
||||
<description>schafkopf-client</description>
|
||||
<properties>
|
||||
<ktor_version>2.3.12</ktor_version>
|
||||
<kotlin.code.style>official</kotlin.code.style>
|
||||
<kotlin_version>2.0.0</kotlin_version>
|
||||
<logback_version>1.4.14</logback_version>
|
||||
<slf4j_version>2.0.9</slf4j_version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<kotlin.compiler.incremental>true</kotlin.compiler.incremental>
|
||||
<main.class>de.heiserer.SchafkopfClientKt</main.class>
|
||||
</properties>
|
||||
<repositories>
|
||||
</repositories>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.ktor</groupId>
|
||||
<artifactId>ktor-server-core-jvm</artifactId>
|
||||
<version>${ktor_version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.ktor</groupId>
|
||||
<artifactId>ktor-server-netty-jvm</artifactId>
|
||||
<version>${ktor_version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>${logback_version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>${slf4j_version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.ktor</groupId>
|
||||
<artifactId>ktor-server-tests-jvm</artifactId>
|
||||
<version>${ktor_version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-test-junit</artifactId>
|
||||
<version>${kotlin_version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlinx</groupId>
|
||||
<artifactId>kotlinx-coroutines-debug</artifactId>
|
||||
<version>1.6.4</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.heiserer</groupId>
|
||||
<artifactId>schafkopf-shared</artifactId>
|
||||
<version>0.0.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<sourceDirectory>src/main/kotlin</sourceDirectory>
|
||||
<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
</resource>
|
||||
</resources>
|
||||
|
||||
<artifactId>schafkopf-client</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.schafkopf</groupId>
|
||||
<artifactId>schafkopf-shared</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<finalName>schafkopf-client-build</finalName>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<version>3.3.0</version>
|
||||
<configuration>
|
||||
<descriptorRefs>
|
||||
<descriptorRef>jar-with-dependencies</descriptorRef>
|
||||
</descriptorRefs>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>org.schafkopf.SchafkopfClient</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>make-assembly</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>kotlin-maven-plugin</artifactId>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<version>${kotlin_version}</version>
|
||||
<configuration>
|
||||
<jvmTarget>1.8</jvmTarget>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>compile</id>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>test-compile</id>
|
||||
<phase>test-compile</phase>
|
||||
<goals>
|
||||
<goal>test-compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<version>1.2.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>java</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<mainClass>${main.class}</mainClass>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<version>2.6</version>
|
||||
<configuration>
|
||||
<descriptorRefs>
|
||||
<descriptorRef>jar-with-dependencies</descriptorRef>
|
||||
</descriptorRefs>
|
||||
<archive>
|
||||
<manifest>
|
||||
<addClasspath>true</addClasspath>
|
||||
<mainClass>${main.class}</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>assemble-all</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -1,139 +0,0 @@
|
||||
package org.schafkopf;
|
||||
|
||||
import jakarta.servlet.DispatcherType;
|
||||
import java.awt.Desktop;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.servlet.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.SchafkopfMessage.SchafkopfBaseMessage;
|
||||
|
||||
/**
|
||||
* Main Class that represents the Backend Server.
|
||||
*/
|
||||
public class BackendServer implements MessageSender {
|
||||
|
||||
private final Server server;
|
||||
|
||||
private final List<FrontendEndpoint> frontendEndpoints = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Creates an Instance of the Backend Server.
|
||||
*/
|
||||
public BackendServer(String hostName, int port, boolean openFrontend,
|
||||
MessageListener messageListener) throws Exception {
|
||||
server = new Server();
|
||||
|
||||
ServerConnector connector = new ServerConnector(server);
|
||||
connector.setHost(hostName);
|
||||
connector.setPort(port);
|
||||
server.addConnector(connector);
|
||||
|
||||
// Setup the basic application "context" for this application at "/"
|
||||
// This is also known as the handler tree (in jetty speak)
|
||||
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
|
||||
context.setContextPath("/");
|
||||
server.setHandler(context);
|
||||
|
||||
// Configure CORS settings
|
||||
configureCors(context);
|
||||
|
||||
URL webContentUrl = getClass().getClassLoader().getResource("web-content");
|
||||
if (webContentUrl == null) {
|
||||
throw new RuntimeException("Unable to find 'web-content' directory");
|
||||
}
|
||||
String webContentPath = webContentUrl.toExternalForm();
|
||||
context.setResourceBase(webContentPath);
|
||||
context.addServlet(new ServletHolder("frontend", DefaultServlet.class), "/");
|
||||
|
||||
// Configure specific websocket behavior
|
||||
JettyWebSocketServletContainerInitializer.configure(
|
||||
context,
|
||||
(servletContext, wsContainer) -> {
|
||||
// Configure default max size
|
||||
wsContainer.setMaxTextMessageSize(65535);
|
||||
wsContainer.setIdleTimeout(Duration.ofDays(300000));
|
||||
// Add websockets
|
||||
wsContainer.addMapping("/schafkopf-events/*",
|
||||
new FrontendEndpointCreator(this, messageListener));
|
||||
});
|
||||
|
||||
if (openFrontend) {
|
||||
URI uri = new URI("http://" + hostName + ":" + port); // Replace with your target URL
|
||||
Desktop.getDesktop().browse(uri);
|
||||
}
|
||||
|
||||
// Start the server in a separate thread
|
||||
Thread serverThread = new Thread(() -> {
|
||||
try {
|
||||
server.start();
|
||||
server.join(); // Wait for server to finish execution
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
serverThread.start();
|
||||
}
|
||||
|
||||
private void configureCors(ServletContextHandler context) {
|
||||
// Enable CORS for all paths
|
||||
FilterHolder cors = context.addFilter(CrossOriginFilter.class, "/*", null);
|
||||
|
||||
// Configure allowed origins, headers, and methods
|
||||
cors.setInitParameter("allowedOrigins", "*");
|
||||
cors.setInitParameter("allowedHeaders", "X-Requested-With,Content-Type,Accept,Origin");
|
||||
cors.setInitParameter("allowedMethods", "GET,POST,PUT,DELETE,OPTIONS");
|
||||
|
||||
// Add filter mappings
|
||||
EnumSet<DispatcherType> types = EnumSet.of(DispatcherType.REQUEST);
|
||||
context.addFilter(cors, "*", types);
|
||||
}
|
||||
|
||||
private void start() throws Exception {
|
||||
server.start();
|
||||
}
|
||||
|
||||
private void join() throws InterruptedException {
|
||||
server.join();
|
||||
}
|
||||
|
||||
public void addFrontendEndpoint(FrontendEndpoint endpoint) {
|
||||
frontendEndpoints.add(endpoint);
|
||||
}
|
||||
|
||||
public void removeFrontendEndpoint(FrontendEndpoint endpoint) {
|
||||
frontendEndpoints.remove(endpoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends Message to all Frontend Instances.
|
||||
*
|
||||
* @param message Message to send (String).
|
||||
*/
|
||||
private void sendMessageToAllFrontendEndpoints(String message) {
|
||||
for (FrontendEndpoint endpoint : frontendEndpoints) {
|
||||
endpoint.sendMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(SchafkopfBaseMessage message) {
|
||||
sendMessageToAllFrontendEndpoints(
|
||||
message.getBaseMessage().toString());
|
||||
}
|
||||
|
||||
|
||||
public void sendMessageTest(String message) {
|
||||
sendMessageToAllFrontendEndpoints(message);
|
||||
}
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
package org.schafkopf;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
|
||||
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
|
||||
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||
import org.schafkopf.SchafkopfMessage.SchafkopfBaseMessage;
|
||||
import org.schafkopf.SchafkopfMessage.SchafkopfMessageOrigin;
|
||||
|
||||
/**
|
||||
* Main Class that represents the Backend Server.
|
||||
*/
|
||||
@WebSocket
|
||||
public class DedicatedServerConnection implements MessageSender {
|
||||
|
||||
private final MessageListener messageListener;
|
||||
private final CountDownLatch closeLatch;
|
||||
private final CountDownLatch connectionLatch;
|
||||
private static Session session;
|
||||
|
||||
/**
|
||||
* Class that represents one Frontend Connection.
|
||||
*/
|
||||
public DedicatedServerConnection(String address, MessageListener messageListener) {
|
||||
this.messageListener = messageListener;
|
||||
this.closeLatch = new CountDownLatch(1);
|
||||
this.connectionLatch = new CountDownLatch(1);
|
||||
|
||||
connect("ws://" + address);
|
||||
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;
|
||||
connectionLatch.countDown();
|
||||
}
|
||||
|
||||
@OnWebSocketMessage
|
||||
public void onMessage(String message) {
|
||||
messageListener.receiveMessage(message);
|
||||
}
|
||||
|
||||
@OnWebSocketClose
|
||||
public void onClose(int statusCode, String reason) {
|
||||
System.out.println("Connection closed: " + reason);
|
||||
closeLatch.countDown();
|
||||
}
|
||||
|
||||
@OnWebSocketError
|
||||
public void onError(Throwable cause) {
|
||||
System.err.println("Error occurred: " + cause.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Main Class that represents the Backend Server.
|
||||
*/
|
||||
@Override
|
||||
public void sendMessage(SchafkopfBaseMessage message) {
|
||||
try {
|
||||
session.getRemote().sendString(
|
||||
new SchafkopfMessage(SchafkopfMessageOrigin.BACKEND, message).getMessageAsString());
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error sending message: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void awaitClose() throws InterruptedException {
|
||||
closeLatch.await();
|
||||
}
|
||||
|
||||
/**
|
||||
* Main Class that represents the Backend Server.
|
||||
*/
|
||||
public void connect(String serverUri) {
|
||||
Thread connectionThread = new Thread(() -> {
|
||||
try {
|
||||
WebSocketClient client = new WebSocketClient();
|
||||
try {
|
||||
client.start();
|
||||
HeartbeatSender heartbeatSender = new HeartbeatSender(this);
|
||||
heartbeatSender.start(); // Start sending heartbeat messages
|
||||
URI uri = new URI(serverUri);
|
||||
ClientUpgradeRequest request = new ClientUpgradeRequest();
|
||||
client.connect(this, uri, request);
|
||||
|
||||
System.out.println("Connecting to : " + uri);
|
||||
this.awaitClose();
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error connecting to server: " + e.getMessage());
|
||||
} finally {
|
||||
try {
|
||||
client.stop();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error starting dedicated server connection: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
connectionThread.start();
|
||||
}
|
||||
}
|
||||
@@ -1,72 +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;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
String clientIp = session.getRemoteAddress().toString();
|
||||
System.out.println("Endpoint connected from ip: " + clientIp);
|
||||
|
||||
backendServer.addFrontendEndpoint(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWebSocketText(String message) {
|
||||
super.onWebSocketText(message);
|
||||
if (messageListener != null) {
|
||||
messageListener.receiveMessage(message); // Notify the listener
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWebSocketClose(int statusCode, String reason) {
|
||||
super.onWebSocketClose(statusCode, reason);
|
||||
|
||||
backendServer.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.
|
||||
*/
|
||||
public void sendMessage(String message) {
|
||||
try {
|
||||
getRemote().sendString(message);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package org.schafkopf;
|
||||
|
||||
import org.eclipse.jetty.websocket.server.JettyServerUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.server.JettyServerUpgradeResponse;
|
||||
import org.eclipse.jetty.websocket.server.JettyWebSocketCreator;
|
||||
|
||||
/**
|
||||
* Creater to make new Instances of the FrontendConnection.
|
||||
*/
|
||||
public class FrontendEndpointCreator implements JettyWebSocketCreator {
|
||||
private BackendServer backendServer;
|
||||
private final MessageListener messageListener;
|
||||
|
||||
public FrontendEndpointCreator(BackendServer backendServer, MessageListener messageListener) {
|
||||
this.messageListener = messageListener;
|
||||
this.backendServer = backendServer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object createWebSocket(
|
||||
JettyServerUpgradeRequest jettyServerUpgradeRequest,
|
||||
JettyServerUpgradeResponse jettyServerUpgradeResponse) {
|
||||
return new FrontendEndpoint(this.backendServer, messageListener);
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package org.schafkopf;
|
||||
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import org.schafkopf.SchafkopfMessage.SchafkopfBaseMessage;
|
||||
|
||||
/**
|
||||
* Creates an Instance of the Backend Server.
|
||||
*/
|
||||
public class HeartbeatSender {
|
||||
|
||||
private static final int HEARTBEAT_INTERVAL = 15000; // 1 minute
|
||||
|
||||
private final DedicatedServerConnection client;
|
||||
|
||||
public HeartbeatSender(DedicatedServerConnection client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an Instance of the Backend Server.
|
||||
*/
|
||||
public void start() {
|
||||
Timer timer = new Timer();
|
||||
timer.scheduleAtFixedRate(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
client.sendMessage(
|
||||
new SchafkopfBaseMessage(SchafkopfMessage.SchafkopfMessageType.HEARTBEAT_SYN));
|
||||
}
|
||||
}, HEARTBEAT_INTERVAL, HEARTBEAT_INTERVAL);
|
||||
}
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
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" or "dedicated_server"
|
||||
String origin = jsonObject.get("origin").getAsString();
|
||||
switch (SchafkopfMessageOrigin.valueOf(origin)) {
|
||||
case FRONTEND:
|
||||
handleFrontendMessage(jsonObject);
|
||||
break;
|
||||
case DEDICATED_SERVER:
|
||||
handleDedicatedServerMessage(jsonObject);
|
||||
break;
|
||||
default:
|
||||
// Handle messages from unknown origins
|
||||
System.out.println("Received message from unknown origin: " + origin);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void handleFrontendMessage(JsonObject jsonObject) {
|
||||
JsonObject message = jsonObject.getAsJsonObject("message");
|
||||
JsonObject content = message.getAsJsonObject("content");
|
||||
String messageType = message.get("message_type").getAsString();
|
||||
|
||||
switch (SchafkopfMessageType.valueOf(messageType)) {
|
||||
case REQUEST_SERVER_CONNECTION:
|
||||
dedicatedServerConnection = new DedicatedServerConnection(
|
||||
content.get("serverAddress").getAsString(),
|
||||
this);
|
||||
break;
|
||||
case PLAYER_CARD:
|
||||
case CREATE_ONLINE_GAME:
|
||||
case JOIN_ONLINE_GAME:
|
||||
case SET_PLAYER_NAME:
|
||||
dedicatedServerConnection.sendMessage(
|
||||
new SchafkopfBaseMessage(SchafkopfMessageType.valueOf(messageType), content));
|
||||
break;
|
||||
case LIST_ONLINE_GAMES:
|
||||
case GET_ONLINE_GAME:
|
||||
case SET_STATUS_READY:
|
||||
case LEAVE_ONLINE_GAME:
|
||||
case START_DEDICATED_GAME:
|
||||
dedicatedServerConnection.sendMessage(
|
||||
new SchafkopfBaseMessage(SchafkopfMessageType.valueOf(messageType)));
|
||||
break;
|
||||
default:
|
||||
// Handle unknown message types
|
||||
System.out.println("Received unknown message type from frontend server: " + messageType);
|
||||
break;
|
||||
}
|
||||
|
||||
System.out.println("Received message from frontend: " + jsonObject);
|
||||
}
|
||||
|
||||
private void handleDedicatedServerMessage(JsonObject jsonObject) {
|
||||
JsonObject message = jsonObject.getAsJsonObject("message");
|
||||
JsonObject content = message.getAsJsonObject("content");
|
||||
String messageType = message.get("message_type").getAsString();
|
||||
|
||||
switch (SchafkopfMessageType.valueOf(messageType)) {
|
||||
case GET_CARD_ONLINE_PLAYER:
|
||||
case HEARTBEAT_ACK:
|
||||
|
||||
break;
|
||||
case GAME_STATE:
|
||||
case ONLINE_PLAYER_HAND:
|
||||
case UNKNOWN_ERROR:
|
||||
case INFO_MESSAGE:
|
||||
case GET_ONLINE_GAME:
|
||||
case LIST_ONLINE_GAMES:
|
||||
backendServer.sendMessage(
|
||||
new SchafkopfBaseMessage(SchafkopfMessageType.valueOf(messageType), content));
|
||||
break;
|
||||
case SERVER_CONNECTION_SUCCESSFUL:
|
||||
case GAME_START_READY:
|
||||
backendServer.sendMessage(
|
||||
new SchafkopfBaseMessage(SchafkopfMessageType.valueOf(messageType)));
|
||||
break;
|
||||
default:
|
||||
// Handle unknown message types
|
||||
System.out.println("Received unknown message type from dedicated server: " + messageType);
|
||||
break;
|
||||
}
|
||||
if (!messageType.equals("HEARTBEAT_ACK")) {
|
||||
System.out.println("Received message from dedicated server: " + jsonObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
package org.schafkopf.cardreader;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
/** Class that represents one Card Reader. */
|
||||
public abstract class CardReader {
|
||||
|
||||
private CountDownLatch nfcLatch = new CountDownLatch(1);
|
||||
private Boolean readingMode = false;
|
||||
private String uidString = "";
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
package org.schafkopf.cardreader;
|
||||
|
||||
import com.fazecast.jSerialComm.SerialPort;
|
||||
import com.sun.tools.jconsole.JConsoleContext;
|
||||
import io.github.cdimascio.dotenv.Dotenv;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import org.schafkopf.BackendServer;
|
||||
|
||||
/** Class that represents the NFC Reader. */
|
||||
public class UsbCardReader extends CardReader {
|
||||
|
||||
private volatile boolean isRunning = true;
|
||||
Dotenv dotenv = Dotenv.configure().directory("./").load();
|
||||
private String comPort = null;
|
||||
|
||||
/**
|
||||
* Creates an Instance of the KartenLeser.
|
||||
*
|
||||
*/
|
||||
public UsbCardReader() {
|
||||
new Thread(this::run).start();
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
isRunning = false;
|
||||
}
|
||||
|
||||
/** Run the reader. */
|
||||
public void run() {
|
||||
// SerialPort[] ports = SerialPort.getCommPorts();
|
||||
//
|
||||
// for (SerialPort port : ports) {
|
||||
// if (port.openPort()) {
|
||||
// System.out.println(port.getSystemPortName());
|
||||
// try {
|
||||
// Thread.sleep(5000);
|
||||
// } catch (InterruptedException e) {
|
||||
// throw new RuntimeException(e);
|
||||
// }
|
||||
// // Read any data available on the serial port
|
||||
// byte[] initialBuffer = new byte[port.bytesAvailable()];
|
||||
// int initialBytesRead = port.readBytes(initialBuffer, initialBuffer.length);
|
||||
// String initialData = null;
|
||||
// try {
|
||||
// initialData = new String(initialBuffer, 0, initialBytesRead, "UTF-8").trim();
|
||||
// } catch (UnsupportedEncodingException e) {
|
||||
// throw new RuntimeException(e);
|
||||
// }
|
||||
// System.out.print("Raw data: ");
|
||||
// for (byte b : initialBuffer) {
|
||||
// System.out.print(b + " ");
|
||||
// }
|
||||
// System.out.println(initialData);
|
||||
// if (initialData.contains("Adafruit PN532 NFC Marker")) {
|
||||
// comPort = port.getSystemPortName();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
comPort = dotenv.get("COM_PORT");
|
||||
|
||||
if (comPort == null) {
|
||||
System.out.println("Adafruit PN532 NFC device not found");
|
||||
return;
|
||||
}
|
||||
|
||||
SerialPort serialPort = SerialPort.getCommPort(comPort);
|
||||
serialPort.setBaudRate(115200);
|
||||
|
||||
if (serialPort.openPort()) {
|
||||
System.out.println("Serial port opened successfully");
|
||||
|
||||
try {
|
||||
while (isRunning) {
|
||||
if (serialPort.bytesAvailable() > 0) {
|
||||
byte[] buffer = new byte[serialPort.bytesAvailable()];
|
||||
int bytesRead = serialPort.readBytes(buffer, buffer.length);
|
||||
String data = new String(buffer, 0, bytesRead, "UTF-8").trim();
|
||||
|
||||
// Process the received data
|
||||
this.nfcGelesen(data);
|
||||
}
|
||||
|
||||
// Optional: Add a delay to avoid consuming too much CPU
|
||||
Thread.sleep(100);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
serialPort.closePort();
|
||||
System.out.println("Serial port closed");
|
||||
}
|
||||
} else {
|
||||
System.out.println("Failed to open serial port");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
package org.schafkopf.player;
|
||||
|
||||
import org.schafkopf.MessageSender;
|
||||
import org.schafkopf.SchafkopfMessage.SchafkopfBaseMessage;
|
||||
import org.schafkopf.cardreader.CardReader;
|
||||
import org.schafkopf.karte.Karte;
|
||||
import org.schafkopf.karte.KartenListe;
|
||||
import org.schafkopf.karte.KartenUtil;
|
||||
import org.schafkopf.spielcontroller.SpielController;
|
||||
|
||||
/**
|
||||
* Player that plays in real life.
|
||||
*/
|
||||
public class LocalPlayer extends Player {
|
||||
|
||||
private final CardReader cardReader;
|
||||
|
||||
public LocalPlayer(CardReader cardReader, MessageSender messageSender) {
|
||||
super("Local Player", messageSender);
|
||||
this.cardReader = cardReader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Karte play(SpielController spiel, KartenListe tischKarten, KartenListe gespielteKarten) {
|
||||
return wartetAufKarte();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetReady() {
|
||||
// Not needed
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for a Card and returns a Karte Object.
|
||||
*/
|
||||
private Karte wartetAufKarte() {
|
||||
String uid = null;
|
||||
System.out.println("Starte Warten auf Karte");
|
||||
try {
|
||||
uid = cardReader.waitForCardScan();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
Karte karte = KartenUtil.getIdOfUid(uid);
|
||||
|
||||
if (karte == null) {
|
||||
System.out.println("Ungültige Karte");
|
||||
return wartetAufKarte();
|
||||
}
|
||||
System.out.println("Karte gescannt: " + karte.getName());
|
||||
System.out.println("Beende Warten auf Karte");
|
||||
return karte;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(SchafkopfBaseMessage message) {
|
||||
System.out.println("LocalPlayer: " + message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package de.heiserer
|
||||
|
||||
import de.heiserer.plugins.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.engine.*
|
||||
import io.ktor.server.netty.*
|
||||
|
||||
fun main() {
|
||||
val test = SchafkopfGameController()
|
||||
|
||||
test.playRound()
|
||||
|
||||
embeddedServer(Netty, port = 8080, host = "0.0.0.0", module = Application::module)
|
||||
.start(wait = true)
|
||||
}
|
||||
|
||||
fun Application.module() {
|
||||
configureRouting()
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package de.heiserer.plugins
|
||||
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.routing.*
|
||||
|
||||
fun Application.configureRouting() {
|
||||
routing {
|
||||
get("/") {
|
||||
call.respondText("Hello World!")
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user