From a9e4d3cbaf18a587a6e73dc8dd15532cda6c57ea Mon Sep 17 00:00:00 2001 From: Snoweuph Date: Wed, 12 Mar 2025 14:34:55 +0100 Subject: [PATCH] Chore: Refactoring of Match Data Structure --- .../de/towerdefence/server/match/Enemy.java | 13 ++ .../server/match/GameSession.java | 45 ++++++ .../de/towerdefence/server/match/Match.java | 141 +++++------------- .../server/match/MatchService.java | 64 +++++--- .../de/towerdefence/server/match/Tower.java | 5 + .../callbacks}/PlayerMoneyCallback.java | 2 +- .../InvalidPlacementException.java | 3 +- .../exeptions}/InvalidPlacementReason.java | 3 +- .../match/exeptions/NotInMatchException.java | 4 + .../server/server/JsonWebsocketHandler.java | 1 + .../channels/match/MatchWebsocketHandler.java | 13 +- .../channels/match/PlayerMoneyMessage.java | 26 ++++ .../placing/InvalidPlacementMessage.java | 9 +- .../server/channels/match/placing/Tower.java | 4 - src/main/resources/spotbugs-exclude.xml | 6 + ws/ws.yml | 1 + 16 files changed, 204 insertions(+), 136 deletions(-) create mode 100644 src/main/java/de/towerdefence/server/match/Enemy.java create mode 100644 src/main/java/de/towerdefence/server/match/GameSession.java create mode 100644 src/main/java/de/towerdefence/server/match/Tower.java rename src/main/java/de/towerdefence/server/{server/channels/match/money => match/callbacks}/PlayerMoneyCallback.java (72%) rename src/main/java/de/towerdefence/server/match/{ => exeptions}/InvalidPlacementException.java (62%) rename src/main/java/de/towerdefence/server/{server/channels/match/placing => match/exeptions}/InvalidPlacementReason.java (74%) create mode 100644 src/main/java/de/towerdefence/server/match/exeptions/NotInMatchException.java create mode 100644 src/main/java/de/towerdefence/server/server/channels/match/PlayerMoneyMessage.java delete mode 100644 src/main/java/de/towerdefence/server/server/channels/match/placing/Tower.java diff --git a/src/main/java/de/towerdefence/server/match/Enemy.java b/src/main/java/de/towerdefence/server/match/Enemy.java new file mode 100644 index 0000000..c1997ff --- /dev/null +++ b/src/main/java/de/towerdefence/server/match/Enemy.java @@ -0,0 +1,13 @@ +package de.towerdefence.server.match; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +public class Enemy { + private int poxX; + private int posY; +} diff --git a/src/main/java/de/towerdefence/server/match/GameSession.java b/src/main/java/de/towerdefence/server/match/GameSession.java new file mode 100644 index 0000000..050ed31 --- /dev/null +++ b/src/main/java/de/towerdefence/server/match/GameSession.java @@ -0,0 +1,45 @@ +package de.towerdefence.server.match; + +import de.towerdefence.server.match.callbacks.PlayerMoneyCallback; +import de.towerdefence.server.match.exeptions.InvalidPlacementException; +import de.towerdefence.server.match.exeptions.InvalidPlacementReason; +import de.towerdefence.server.player.Player; +import lombok.Getter; + +public class GameSession { + final static int START_MONEY = 50; + final static int MAP_SIZE_X = 10; + final static int MAP_SIZE_Y = 20; + + private final Tower[][] towers = new Tower[MAP_SIZE_X][MAP_SIZE_Y]; + + private final Player player; + @Getter + private int money; + private final PlayerMoneyCallback moneyCallback; + + public GameSession(Player player, PlayerMoneyCallback moneyCallback) { + this.player = player; + this.moneyCallback = moneyCallback; + this.money = START_MONEY; + } + + public void placeTower(Tower tower, int x, int y) throws InvalidPlacementException { + if (x < 0 || y < 0 || x + 1 > MAP_SIZE_X || y + 1 > MAP_SIZE_Y) { + throw new InvalidPlacementException(InvalidPlacementReason.OUT_OF_BOUNDS); + } + if (towers[x][y] != null) { + throw new InvalidPlacementException(InvalidPlacementReason.LOCATION_USED); + } + if (money < Tower.COST) { + throw new InvalidPlacementException(InvalidPlacementReason.NOT_ENOUGH_MONEY); + } + money -= Tower.COST; + moneyCallback.call(player, money); + towers[x][y] = tower; + } + + public void addMoney(int amount) { + money += amount; + } +} diff --git a/src/main/java/de/towerdefence/server/match/Match.java b/src/main/java/de/towerdefence/server/match/Match.java index 7deb5d6..fb4ff50 100644 --- a/src/main/java/de/towerdefence/server/match/Match.java +++ b/src/main/java/de/towerdefence/server/match/Match.java @@ -1,9 +1,7 @@ package de.towerdefence.server.match; +import de.towerdefence.server.match.callbacks.PlayerMoneyCallback; import de.towerdefence.server.player.Player; -import de.towerdefence.server.server.channels.match.money.PlayerMoneyCallback; -import de.towerdefence.server.server.channels.match.placing.InvalidPlacementReason; -import de.towerdefence.server.server.channels.match.placing.Tower; import lombok.Getter; import java.util.Optional; @@ -11,21 +9,12 @@ import java.util.Optional; @Getter public class Match { - public static final int TOWER_PRICE = 20; - public final static int MAP_SIZE_X = 10; - public final static int MAP_SIZE_Y = 20; - public final static int START_MONEY = 50; private final String matchId; private final Player player1; private final Player player2; - private int player1Money = START_MONEY; - private int player2Money = START_MONEY; - private PlayerMoneyCallback player1MoneyCallback; - private PlayerMoneyCallback player2MoneyCallback; - private final Tower[][] player1Map = new Tower[MAP_SIZE_X][MAP_SIZE_Y]; - private final Tower[][] player2Map = new Tower[MAP_SIZE_X][MAP_SIZE_Y]; - private boolean isPlayer1Connected = false; - private boolean isPlayer2Connected = false; + + private GameSession player1Session; + private GameSession player2Session; public Match(String matchId, Player player1, Player player2) { this.matchId = matchId; @@ -33,100 +22,48 @@ public class Match { this.player2 = player2; } - public Player getOpponent(Player player) { + public Optional getOpponent(Player player) { boolean isPlayer1 = player1.equals(player); boolean isPlayer2 = player2.equals(player); if (!isPlayer1 && !isPlayer2) { - return null; - } - if (isPlayer1) { - return player2; - } - return player1; - } - - /** - * @return opponent - */ - public Player addTower(Player player, Tower tower, int x, int y) throws InvalidPlacementException { - if (player != player1 && player != player2) { - return null; - } - if (x < 0 || y < 0 || x + 1 > Match.MAP_SIZE_X || y + 1 > Match.MAP_SIZE_Y) { - throw new InvalidPlacementException(InvalidPlacementReason.OUT_OF_BOUNDS); - } - if (player == player1) { - if (player1Map[x][y] != null) { - throw new InvalidPlacementException(InvalidPlacementReason.LOCATION_USED); - } - removeMoney(player, TOWER_PRICE); - player1MoneyCallback.call(player1, player1Money); - player1Map[x][y] = tower; - } else { - if (player2Map[x][y] != null) { - throw new InvalidPlacementException(InvalidPlacementReason.LOCATION_USED); - } - removeMoney(player, TOWER_PRICE); - player2MoneyCallback.call(player2, player2Money); - player2Map[x][y] = tower; - } - return getOpponent(player); - } - - // INFO: MoneyHandling - - public Optional getPlayerMoney(Player player) { - if (player != player1 && player != player2) { return Optional.empty(); } - if (player == player1) { - return Optional.of(player1Money); - } else { - return Optional.of(player2Money); + if (isPlayer1) { + return Optional.of(player2); + } + return Optional.of(player1); + } + + public Optional getPlayerGameSession(Player player) { + boolean isPlayer1 = player1.equals(player); + boolean isPlayer2 = player2.equals(player); + if (!isPlayer1 && !isPlayer2) { + return Optional.empty(); + } + if (isPlayer1) { + return Optional.of(player1Session); + } + return Optional.of(player2Session); + + } + + public void connectPlayer(Player player, PlayerMoneyCallback moneyCallback) { + boolean isPlayer1 = player1.equals(player); + boolean isPlayer2 = player2.equals(player); + if (!isPlayer1 && !isPlayer2) { + return; + } + if (isPlayer1 && player1Session == null) { + this.player1Session = new GameSession(player, moneyCallback); + return; + } + if (isPlayer2 && player2Session == null) { + this.player2Session = new GameSession(player, moneyCallback); + return; } } - public void addMoney(int money) { - player1Money += money; - player2Money += money; - } - - public void removeMoney(Player player, int price) throws InvalidPlacementException { - if (player == player1) { - if (price < player1Money) { - player1Money -= price; - } else { - throw new InvalidPlacementException(InvalidPlacementReason.NOT_ENOUGH_MONEY); - } - } - if (player == player2) { - if (price < player2Money) { - player2Money -= price; - } else { - throw new InvalidPlacementException(InvalidPlacementReason.NOT_ENOUGH_MONEY); - } - } - } - - public void setPlayerMoneyCallback(Player player, PlayerMoneyCallback playerMoneyCallback) { - if (player == player1) { - this.player1MoneyCallback = playerMoneyCallback; - } - if (player == player2) { - this.player2MoneyCallback = playerMoneyCallback; - } - } - - /** - * @return true if both players are connected - */ - public boolean setPlayerConnected(Player player) { - if (player == player1) { - isPlayer1Connected = true; - } - if (player == player2) { - isPlayer2Connected = true; - } - return isPlayer1Connected && isPlayer2Connected; + public boolean hasMatchStarted() { + return player1Session != null && player2Session != null; } } diff --git a/src/main/java/de/towerdefence/server/match/MatchService.java b/src/main/java/de/towerdefence/server/match/MatchService.java index 234392c..dacb7d9 100644 --- a/src/main/java/de/towerdefence/server/match/MatchService.java +++ b/src/main/java/de/towerdefence/server/match/MatchService.java @@ -1,8 +1,10 @@ package de.towerdefence.server.match; +import de.towerdefence.server.match.callbacks.PlayerMoneyCallback; +import de.towerdefence.server.match.exeptions.InvalidPlacementException; +import de.towerdefence.server.match.exeptions.InvalidPlacementReason; +import de.towerdefence.server.match.exeptions.NotInMatchException; import de.towerdefence.server.player.Player; -import de.towerdefence.server.server.channels.match.money.PlayerMoneyCallback; -import de.towerdefence.server.server.channels.match.placing.Tower; import org.springframework.stereotype.Service; import java.util.HashMap; @@ -29,33 +31,53 @@ public class MatchService { /** * @return opponent */ - public Player placeTower(Player player, int x, int y) throws InvalidPlacementException{ - return playerMatches.get(player).addTower(player, new Tower(), x, y); + public Player placeTower(Player player, int x, int y) throws NotInMatchException, InvalidPlacementException { + Match match = playerMatches.get(player); + if (!match.hasMatchStarted()) { + throw new InvalidPlacementException(InvalidPlacementReason.MATCH_NOT_STARTED); + } + Optional playerSession = match.getPlayerGameSession(player); + if (playerSession.isEmpty()) { + throw new NotInMatchException(); + } + Optional opponent = match.getOpponent(player); + if (opponent.isEmpty()) { + throw new NotInMatchException(); + } + playerSession.get().placeTower(new Tower(), x, y); + return opponent.get(); } - - public void playerConnected(Player player, PlayerMoneyCallback callback) { + public void playerConnected(Player player, PlayerMoneyCallback moneyCallback) { Match match = playerMatches.get(player); - Optional currentMoney = match.getPlayerMoney(player); - if (currentMoney.isEmpty()) { + match.connectPlayer(player, moneyCallback); + Optional optionalPlayerSession = match.getPlayerGameSession(player); + if (optionalPlayerSession.isEmpty()) { return; } - match.setPlayerMoneyCallback(player, callback); - callback.call(player, currentMoney.get()); - boolean matchStarted =match.setPlayerConnected(player); - if (!matchStarted) { + GameSession playerSession = optionalPlayerSession.get(); + moneyCallback.call(player, playerSession.getMoney()); + if (!match.hasMatchStarted()) { return; } + Optional optionalOpponent = match.getOpponent(player); + if (optionalOpponent.isEmpty()) { + return; + } + Player opponent = optionalOpponent.get(); + Optional optionalOpponentSession = match.getPlayerGameSession(opponent); + if (optionalOpponentSession.isEmpty()) { + return; + } + GameSession opponentSession = optionalOpponentSession.get(); ScheduledFuture moneyTask = scheduler.scheduleAtFixedRate( - () -> { - match.addMoney(3); - match.getPlayer1MoneyCallback().call(match.getPlayer1(), match.getPlayer1Money()); - match.getPlayer2MoneyCallback().call(match.getPlayer2(), match.getPlayer2Money()); - }, - 5, - 5, - TimeUnit.SECONDS - ); + () -> { + playerSession.addMoney(3); + opponentSession.addMoney(3); + }, + 5, + 5, + TimeUnit.SECONDS); moneyTasks.put(match, moneyTask); } } diff --git a/src/main/java/de/towerdefence/server/match/Tower.java b/src/main/java/de/towerdefence/server/match/Tower.java new file mode 100644 index 0000000..ae8b146 --- /dev/null +++ b/src/main/java/de/towerdefence/server/match/Tower.java @@ -0,0 +1,5 @@ +package de.towerdefence.server.match; + +public class Tower { + public static final int COST = 20; +} diff --git a/src/main/java/de/towerdefence/server/server/channels/match/money/PlayerMoneyCallback.java b/src/main/java/de/towerdefence/server/match/callbacks/PlayerMoneyCallback.java similarity index 72% rename from src/main/java/de/towerdefence/server/server/channels/match/money/PlayerMoneyCallback.java rename to src/main/java/de/towerdefence/server/match/callbacks/PlayerMoneyCallback.java index dc1fb46..ae35600 100644 --- a/src/main/java/de/towerdefence/server/server/channels/match/money/PlayerMoneyCallback.java +++ b/src/main/java/de/towerdefence/server/match/callbacks/PlayerMoneyCallback.java @@ -1,4 +1,4 @@ -package de.towerdefence.server.server.channels.match.money; +package de.towerdefence.server.match.callbacks; import de.towerdefence.server.player.Player; diff --git a/src/main/java/de/towerdefence/server/match/InvalidPlacementException.java b/src/main/java/de/towerdefence/server/match/exeptions/InvalidPlacementException.java similarity index 62% rename from src/main/java/de/towerdefence/server/match/InvalidPlacementException.java rename to src/main/java/de/towerdefence/server/match/exeptions/InvalidPlacementException.java index ede36f3..eea167d 100644 --- a/src/main/java/de/towerdefence/server/match/InvalidPlacementException.java +++ b/src/main/java/de/towerdefence/server/match/exeptions/InvalidPlacementException.java @@ -1,6 +1,5 @@ -package de.towerdefence.server.match; +package de.towerdefence.server.match.exeptions; -import de.towerdefence.server.server.channels.match.placing.InvalidPlacementReason; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/src/main/java/de/towerdefence/server/server/channels/match/placing/InvalidPlacementReason.java b/src/main/java/de/towerdefence/server/match/exeptions/InvalidPlacementReason.java similarity index 74% rename from src/main/java/de/towerdefence/server/server/channels/match/placing/InvalidPlacementReason.java rename to src/main/java/de/towerdefence/server/match/exeptions/InvalidPlacementReason.java index 5c16215..f741f39 100644 --- a/src/main/java/de/towerdefence/server/server/channels/match/placing/InvalidPlacementReason.java +++ b/src/main/java/de/towerdefence/server/match/exeptions/InvalidPlacementReason.java @@ -1,4 +1,4 @@ -package de.towerdefence.server.server.channels.match.placing; +package de.towerdefence.server.match.exeptions; import lombok.AllArgsConstructor; import lombok.Getter; @@ -6,6 +6,7 @@ import lombok.Getter; @Getter @AllArgsConstructor public enum InvalidPlacementReason { + MATCH_NOT_STARTED("match-not-started"), OUT_OF_BOUNDS("out-of-bounds"), LOCATION_USED("location-used"), NOT_ENOUGH_MONEY("not-enough-money"); diff --git a/src/main/java/de/towerdefence/server/match/exeptions/NotInMatchException.java b/src/main/java/de/towerdefence/server/match/exeptions/NotInMatchException.java new file mode 100644 index 0000000..8297e23 --- /dev/null +++ b/src/main/java/de/towerdefence/server/match/exeptions/NotInMatchException.java @@ -0,0 +1,4 @@ +package de.towerdefence.server.match.exeptions; + +public class NotInMatchException extends RuntimeException { +} diff --git a/src/main/java/de/towerdefence/server/server/JsonWebsocketHandler.java b/src/main/java/de/towerdefence/server/server/JsonWebsocketHandler.java index 29fd481..9359663 100644 --- a/src/main/java/de/towerdefence/server/server/JsonWebsocketHandler.java +++ b/src/main/java/de/towerdefence/server/server/JsonWebsocketHandler.java @@ -39,6 +39,7 @@ public abstract class JsonWebsocketHandler extends TextWebSocketHandler { protected void closeSession(WebSocketSession session, CloseStatus reason){ if(session.isOpen()){ try{ + sessionPlayers.remove(session); session.close(reason); } catch (Exception exception) { logger.info("Unable to Close the Session", exception); diff --git a/src/main/java/de/towerdefence/server/server/channels/match/MatchWebsocketHandler.java b/src/main/java/de/towerdefence/server/server/channels/match/MatchWebsocketHandler.java index c02da6a..f9e48a0 100644 --- a/src/main/java/de/towerdefence/server/server/channels/match/MatchWebsocketHandler.java +++ b/src/main/java/de/towerdefence/server/server/channels/match/MatchWebsocketHandler.java @@ -1,8 +1,9 @@ package de.towerdefence.server.server.channels.match; import com.fasterxml.jackson.databind.ObjectMapper; -import de.towerdefence.server.match.InvalidPlacementException; import de.towerdefence.server.match.MatchService; +import de.towerdefence.server.match.exeptions.InvalidPlacementException; +import de.towerdefence.server.match.exeptions.NotInMatchException; import de.towerdefence.server.player.Player; import de.towerdefence.server.server.JsonWebsocketHandler; import de.towerdefence.server.server.channels.match.money.PlayerMoneyMessage; @@ -47,6 +48,13 @@ public class MatchWebsocketHandler extends JsonWebsocketHandler { } } + @Override + protected void closeSession(WebSocketSession session, CloseStatus reason) { + Player player = sessionPlayers.get(session); + super.closeSession(session, reason); + playerSessions.remove(player); + } + @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) { try { @@ -80,6 +88,9 @@ public class MatchWebsocketHandler extends JsonWebsocketHandler { } catch (InvalidPlacementException exception) { new InvalidPlacementMessage(msg.getX(), msg.getY(), exception.getReason()).send(session); return; + } catch (NotInMatchException ignored) { + this.closeSession(session, CloseStatus.BAD_DATA); + return; } WebSocketSession opponentSession = playerSessions.get(opponent); new TowerPlacedMessage(msg.getX(), msg.getY(), GamePlayerMap.PLAYER).send(session); diff --git a/src/main/java/de/towerdefence/server/server/channels/match/PlayerMoneyMessage.java b/src/main/java/de/towerdefence/server/server/channels/match/PlayerMoneyMessage.java new file mode 100644 index 0000000..cc7428f --- /dev/null +++ b/src/main/java/de/towerdefence/server/server/channels/match/PlayerMoneyMessage.java @@ -0,0 +1,26 @@ +package de.towerdefence.server.server.channels.match; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import de.towerdefence.server.server.JsonMessage; +import lombok.AllArgsConstructor; + +import java.util.Map; + +@AllArgsConstructor +public class PlayerMoneyMessage extends JsonMessage { + + private final int playerMoney; + + @Override + protected String getMessageId() { + return "PlayerMoney"; + } + + @Override + protected Map getData(JsonNodeFactory factory) { + return Map.of( + "playerMoney", factory.numberNode(this.playerMoney) + ); + } +} diff --git a/src/main/java/de/towerdefence/server/server/channels/match/placing/InvalidPlacementMessage.java b/src/main/java/de/towerdefence/server/server/channels/match/placing/InvalidPlacementMessage.java index 9570a47..e96f542 100644 --- a/src/main/java/de/towerdefence/server/server/channels/match/placing/InvalidPlacementMessage.java +++ b/src/main/java/de/towerdefence/server/server/channels/match/placing/InvalidPlacementMessage.java @@ -2,6 +2,8 @@ package de.towerdefence.server.server.channels.match.placing; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.JsonNodeFactory; + +import de.towerdefence.server.match.exeptions.InvalidPlacementReason; import de.towerdefence.server.server.JsonMessage; import lombok.AllArgsConstructor; @@ -22,9 +24,8 @@ public class InvalidPlacementMessage extends JsonMessage { @Override protected Map getData(JsonNodeFactory factory) { return Map.of( - "x", factory.numberNode(this.x), - "y", factory.numberNode(this.y), - "reason", factory.textNode(this.reason.getJsonName()) - ); + "x", factory.numberNode(this.x), + "y", factory.numberNode(this.y), + "reason", factory.textNode(this.reason.getJsonName())); } } diff --git a/src/main/java/de/towerdefence/server/server/channels/match/placing/Tower.java b/src/main/java/de/towerdefence/server/server/channels/match/placing/Tower.java deleted file mode 100644 index d3008e9..0000000 --- a/src/main/java/de/towerdefence/server/server/channels/match/placing/Tower.java +++ /dev/null @@ -1,4 +0,0 @@ -package de.towerdefence.server.server.channels.match.placing; - -public class Tower { -} diff --git a/src/main/resources/spotbugs-exclude.xml b/src/main/resources/spotbugs-exclude.xml index 033d812..d737fbf 100644 --- a/src/main/resources/spotbugs-exclude.xml +++ b/src/main/resources/spotbugs-exclude.xml @@ -19,4 +19,10 @@ + + + + + + diff --git a/ws/ws.yml b/ws/ws.yml index a0a9743..138364a 100644 --- a/ws/ws.yml +++ b/ws/ws.yml @@ -216,6 +216,7 @@ channels: reason: type: string enum: + - "match-not-started" - "out-of-bounds" - "location-used" - "not-enough-money"