Make fit for beta release (#51)

* removed

* edited a lot of stuff
This commit is contained in:
Valentin Heiserer
2024-04-26 01:41:50 +02:00
committed by GitHub
parent 2e5a42b6d3
commit 539e29dc56
94 changed files with 843 additions and 8636 deletions

View File

@@ -1,5 +1,6 @@
package org.schafkopf;
import com.google.gson.JsonArray;
import jakarta.servlet.DispatcherType;
import java.net.DatagramSocket;
import java.net.InetAddress;
@@ -16,6 +17,7 @@ import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlets.CrossOriginFilter;
import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer;
import org.schafkopf.SchafkopfException.NoGameSessionException;
/**
* Main Class that represents the Backend Server.
@@ -29,8 +31,6 @@ public class DedicatedServer {
private final List<GameSession> gameSessions = new ArrayList<>();
private GameSession currentGameSession;
/**
* Creates an Instance of the Backend Server.
*/
@@ -73,10 +73,8 @@ public class DedicatedServer {
wsContainer.setMaxTextMessageSize(65535);
wsContainer.setIdleTimeout(Duration.ofDays(300000));
// Add websockets
wsContainer.addMapping("/*", new FrontendEndpointCreator(this));
wsContainer.addMapping("/*", new SchafkopfClientConnectionCreator(this));
});
currentGameSession = new GameSession();
}
/**
@@ -126,7 +124,44 @@ public class DedicatedServer {
return gameSessions;
}
public GameSession getCurrentGameSession() {
return currentGameSession;
/**
* The main entrypoint of the Application.
*/
public JsonArray getGameSessionsAsJson() throws NoGameSessionException {
if (gameSessions.isEmpty()) {
throw new NoGameSessionException();
}
JsonArray gameSessionsJson = new JsonArray();
for (GameSession gameSession : gameSessions) {
gameSessionsJson.add(gameSession.getJson());
}
return gameSessionsJson;
}
/**
* The main entrypoint of the Application.
*/
public GameSession getCurrentGameSession() throws NoGameSessionException {
if (gameSessions.isEmpty()) {
throw new NoGameSessionException();
}
return gameSessions.get(gameSessions.size() - 1);
}
/**
* The main entrypoint of the Application.
*/
public GameSession getGameSessionByName(String gameId) throws NoGameSessionException {
for (GameSession gameSession : gameSessions) {
if (gameSession.getServerName().equals(gameId)) {
return gameSession;
}
}
throw new NoGameSessionException();
}
public void removeGameSession(GameSession gameSession) {
gameSessions.remove(gameSession);
}
}

View File

@@ -1,68 +1,108 @@
package org.schafkopf;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import java.util.ArrayList;
import java.util.List;
import org.schafkopf.SchafkopfException.NotEnoughPlayersException;
import org.schafkopf.SchafkopfException.PlayerNotReadyException;
import org.schafkopf.SchafkopfMessage.SchafkopfBaseMessage;
import org.schafkopf.SchafkopfMessage.SchafkopfMessageType;
import org.schafkopf.player.BotPlayer;
import org.schafkopf.player.OnlinePlayer;
import org.schafkopf.player.Player;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The main entrypoint of the Application.
*/
public class GameSession implements MessageSender {
private static final Logger logger = LoggerFactory.getLogger(GameSession.class);
private Schafkopf schafkopf;
private List<Player> player;
private List<SchafkopfClientConnection> clients;
private Thread spielThread;
private String serverName;
private DedicatedServer dedicatedServer;
/**
* The main entrypoint of the Application.
*/
public GameSession() {
player = new ArrayList<>();
clients = new ArrayList<>();
public GameSession(String serverName, DedicatedServer dedicatedServer) {
this.dedicatedServer = dedicatedServer;
this.serverName = serverName;
this.clients = new ArrayList<>();
logger.info(serverName + " created.");
}
/**
* Class that represents one Frontend Connection.
*/
public void addPlayer(SchafkopfClientConnection client) {
if (this.player.size() >= 4) {
if (this.clients.size() >= 4) {
throw new RuntimeException("Game is full");
}
System.out.println("Adding player to game: " + client);
logger.info("Adding player to game: " + client);
clients.add(client);
OnlinePlayer onlinePlayer = new OnlinePlayer(client);
this.player.add(onlinePlayer);
OnlinePlayer onlinePlayer = new OnlinePlayer(client, client.getName());
client.setOnlinePlayer(onlinePlayer);
this.sendSessionInfo();
}
public Schafkopf getSchafkopf() {
return schafkopf;
/**
* Class that represents one Frontend Connection.
*/
public void removePlayer(SchafkopfClientConnection client) {
logger.info("Removing player from game: " + client);
clients.remove(client);
if (clients.isEmpty()) {
logger.info("No players left in game: " + serverName);
if (spielThread != null) {
spielThread.interrupt();
}
this.dedicatedServer.removeGameSession(this);
return;
}
this.sendSessionInfo();
}
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());
void startGame() throws PlayerNotReadyException {
logger.info("Starting game: " + serverName + " with " + clients.size() + " players");
List<Player> players = new ArrayList<>();
for (SchafkopfClientConnection client : clients) {
players.add(client.getOnlinePlayer());
}
for (int i = players.size(); i < 4; i++) {
players.add(new BotPlayer("Bot " + i));
}
for (SchafkopfClientConnection client : clients) {
if (!client.isReady()) {
throw new PlayerNotReadyException();
}
}
System.out.println("Starting game with, now: " + this.player.size() + " players");
sendMessage(new SchafkopfBaseMessage(SchafkopfMessageType.GAME_START_READY));
//wait for 5 seconds
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
spielThread = new Thread(() -> {
try {
schafkopf = new Schafkopf(this.player.toArray(new Player[0]), this);
schafkopf = new Schafkopf(players.toArray(Player[]::new), this);
schafkopf.startGame();
clients.forEach(client -> client.resetReady());
} catch (NotEnoughPlayersException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
@@ -72,15 +112,65 @@ public class GameSession implements MessageSender {
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);
}
}
/**
* The main entrypoint of the Application.
*/
public JsonObject getJson() {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("serverName", serverName);
jsonObject.addProperty("playerCount", getPlayerCount());
// Create an array to hold player information
JsonArray playersArray = new JsonArray();
for (SchafkopfClientConnection client : clients) {
JsonObject playerObject = new JsonObject();
playerObject.addProperty("playerName",
client.getName()); // Assuming you have a method to get player name
playerObject.addProperty("isReady",
client.isReady()); // Assuming you have a method to check player readiness
playersArray.add(playerObject);
}
for (int i = clients.size(); i < 4; i++) {
JsonObject playerObject = new JsonObject();
playerObject.addProperty("playerName",
"Bot " + i); // Assuming you have a method to get player name
playerObject.addProperty("isReady",
true);
playerObject.addProperty("isBot",
true);
playersArray.add(playerObject);
}
jsonObject.add("players", playersArray);
return jsonObject;
}
public String getServerName() {
return serverName;
}
public int getPlayerCount() {
return clients.size();
}
/**
* Class that represents one Frontend Connection.
*/
public void sendSessionInfo() {
JsonObject messageObject2 = new JsonObject();
messageObject2.add("game", this.getJson());
sendMessage(
new SchafkopfBaseMessage(SchafkopfMessageType.GET_ONLINE_GAME,
messageObject2));
}
}

View File

@@ -6,18 +6,21 @@ 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.SchafkopfException.NoGameSessionException;
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;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Class that represents one Frontend Connection.
*/
public class SchafkopfClientConnection extends WebSocketAdapter implements MessageSender {
private static final Logger logger = LoggerFactory.getLogger(SchafkopfClientConnection.class);
private final CountDownLatch connectionLatch;
private final CountDownLatch closureLatch = new CountDownLatch(1);
private DedicatedServer dedicatedServer;
@@ -28,13 +31,15 @@ public class SchafkopfClientConnection extends WebSocketAdapter implements Messa
private OnlinePlayer onlinePlayer;
private boolean ready = false;
private String name = "player";
/**
* 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
@@ -42,8 +47,7 @@ public class SchafkopfClientConnection extends WebSocketAdapter implements Messa
this.session = session;
super.onWebSocketConnect(session);
String clientIp = session.getRemoteAddress().toString();
System.out.println("Endpoint connected from ip: " + clientIp);
logger.info("Endpoint connected from ip: " + clientIp);
connectionLatch.countDown();
dedicatedServer.addFrontendEndpoint(this);
sendMessage(new SchafkopfBaseMessage(SchafkopfMessageType.SERVER_CONNECTION_SUCCESSFUL));
@@ -54,44 +58,82 @@ public class SchafkopfClientConnection extends WebSocketAdapter implements Messa
super.onWebSocketText(jsonMessage);
Gson gson = new Gson();
JsonObject jsonObject = gson.fromJson(jsonMessage, JsonObject.class);
// Check if the origin is "frontend"
// Check if the origin is "backend"
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)) {
if (!SchafkopfMessageOrigin.BACKEND.toString().equals(origin)) {
return;
}
JsonObject message = jsonObject.getAsJsonObject("message");
JsonObject content = message.getAsJsonObject("content");
String messageType = message.get("message_type").getAsString();
switch (SchafkopfMessageType.valueOf(messageType)) {
case HEARTBEAT_SYN:
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() + ".");
break;
case JOIN_ONLINE_GAME:
GameSession gameSession = null;
try {
dedicatedServer.getCurrentGameSession().startGame();
} catch (NotEnoughPlayersException e) {
gameSession = dedicatedServer.getGameSessionByName(
content.get("serverName").getAsString());
} catch (NoGameSessionException e) {
JsonObject messageObject = new JsonObject();
messageObject.addProperty("error",
"No GameSession with name \"" + content.get("serverName").getAsString()
+ "\" found.");
sendMessage(new SchafkopfBaseMessage(SchafkopfMessageType.UNKNOWN_ERROR,
"Not enough players to start the game."));
messageObject));
break;
}
} else if (SchafkopfMessageType.PLAYER_CARD.toString().equals(messageType)) {
joinGame(gameSession);
sendServerList();
JsonObject messageObject = new JsonObject();
messageObject.addProperty("message",
"Joined GameSession \"" + gameSession.getServerName() + "\".");
sendMessage(new SchafkopfBaseMessage(SchafkopfMessageType.INFO_MESSAGE,
messageObject));
break;
case START_DEDICATED_GAME:
try {
this.gameSession.startGame();
} catch (SchafkopfException e) {
sendError(e);
}
break;
case SET_PLAYER_NAME:
this.name = content.get("playerName").getAsString();
break;
case PLAYER_CARD:
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);
break;
case LIST_ONLINE_GAMES:
sendServerList();
break;
case GET_ONLINE_GAME:
this.gameSession.sendSessionInfo();
break;
case CREATE_ONLINE_GAME:
String servername = content.get("serverName").getAsString();
GameSession gameSession2 = new GameSession(servername, this.dedicatedServer);
dedicatedServer.addGameSession(gameSession2);
joinGame(gameSession2);
sendServerList();
break;
case SET_STATUS_READY:
ready = !ready;
this.gameSession.sendSessionInfo();
break;
case LEAVE_ONLINE_GAME:
this.gameSession.removePlayer(this);
this.gameSession = null;
sendServerList();
break;
default:
// Handle unknown message types
logger.warn("Received unknown message type: " + messageType);
break;
}
}
@@ -100,13 +142,20 @@ public class SchafkopfClientConnection extends WebSocketAdapter implements Messa
this.onlinePlayer = onlinePlayer;
}
public OnlinePlayer getOnlinePlayer() {
return this.onlinePlayer;
}
@Override
public void onWebSocketClose(int statusCode, String reason) {
if (this.gameSession != null) {
this.gameSession.removePlayer(this);
}
super.onWebSocketClose(statusCode, reason);
dedicatedServer.removeFrontendEndpoint(this);
System.out.println("Socket Closed: [" + statusCode + "] " + reason);
logger.warn("Socket Closed: [" + statusCode + "] " + reason);
closureLatch.countDown();
}
@@ -124,11 +173,61 @@ public class SchafkopfClientConnection extends WebSocketAdapter implements Messa
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();
}
}
/**
* The main entrypoint of the Application.
*/
public void joinGame(GameSession gameSession) {
if (this.gameSession != null) {
this.gameSession.removePlayer(this);
}
this.gameSession = gameSession;
gameSession.addPlayer(this);
}
private void sendServerList() {
JsonObject messageObject = new JsonObject();
try {
messageObject.add("games", dedicatedServer.getGameSessionsAsJson());
} catch (NoGameSessionException e) {
JsonObject error = new JsonObject();
error.addProperty("error",
"No GameSessions found.");
sendMessage(new SchafkopfBaseMessage(SchafkopfMessageType.UNKNOWN_ERROR,
error));
return;
}
sendMessage(new SchafkopfBaseMessage(SchafkopfMessageType.LIST_ONLINE_GAMES,
messageObject));
}
public boolean isReady() {
return ready;
}
public void resetReady() {
ready = false;
}
public String getName() {
return this.name;
}
/**
* Send a message to the connected FrontEnd.
*/
public void sendError(SchafkopfException e) {
JsonObject messageObject = new JsonObject();
messageObject.addProperty("error",
e.getMessage());
sendMessage(new SchafkopfBaseMessage(SchafkopfMessageType.UNKNOWN_ERROR,
messageObject));
}
}

View File

@@ -7,11 +7,11 @@ import org.eclipse.jetty.websocket.server.JettyWebSocketCreator;
/**
* Creater to make new Instances of the FrontendConnection.
*/
public class FrontendEndpointCreator implements JettyWebSocketCreator {
public class SchafkopfClientConnectionCreator implements JettyWebSocketCreator {
private DedicatedServer dedicatedServer;
public FrontendEndpointCreator(DedicatedServer dedicatedServer) {
public SchafkopfClientConnectionCreator(DedicatedServer dedicatedServer) {
this.dedicatedServer = dedicatedServer;
}