From 934c75d6d902e3090327d2dae0897f5944878b4f Mon Sep 17 00:00:00 2001 From: Snoweuph Date: Wed, 12 Mar 2025 15:10:57 +0100 Subject: [PATCH 1/4] TD-5: Split up Match and Gamesession --- .../server/{match => game}/Enemy.java | 2 +- .../server/{match => game}/GameSession.java | 9 ++++---- .../server/{match => game}/Tower.java | 2 +- .../callbacks/PlayerHitpointsCallback.java | 0 .../callbacks/PlayerMoneyCallback.java | 2 +- .../exeptions/InvalidPlacementException.java | 2 +- .../exeptions/InvalidPlacementReason.java | 2 +- .../de/towerdefence/server/match/Match.java | 2 -- .../server/match/MatchService.java | 14 +++++++++++- .../{exeptions => }/NotInMatchException.java | 2 +- .../channels/match/MatchWebsocketHandler.java | 22 ++++++++++--------- .../placing/InvalidPlacementMessage.java | 6 ++--- 12 files changed, 38 insertions(+), 27 deletions(-) rename src/main/java/de/towerdefence/server/{match => game}/Enemy.java (82%) rename src/main/java/de/towerdefence/server/{match => game}/GameSession.java (83%) rename src/main/java/de/towerdefence/server/{match => game}/Tower.java (62%) rename src/main/java/de/towerdefence/server/{match => game}/callbacks/PlayerHitpointsCallback.java (100%) rename src/main/java/de/towerdefence/server/{match => game}/callbacks/PlayerMoneyCallback.java (76%) rename src/main/java/de/towerdefence/server/{match => game}/exeptions/InvalidPlacementException.java (80%) rename src/main/java/de/towerdefence/server/{match => game}/exeptions/InvalidPlacementReason.java (86%) rename src/main/java/de/towerdefence/server/match/{exeptions => }/NotInMatchException.java (56%) diff --git a/src/main/java/de/towerdefence/server/match/Enemy.java b/src/main/java/de/towerdefence/server/game/Enemy.java similarity index 82% rename from src/main/java/de/towerdefence/server/match/Enemy.java rename to src/main/java/de/towerdefence/server/game/Enemy.java index c1997ff..3a7828f 100644 --- a/src/main/java/de/towerdefence/server/match/Enemy.java +++ b/src/main/java/de/towerdefence/server/game/Enemy.java @@ -1,4 +1,4 @@ -package de.towerdefence.server.match; +package de.towerdefence.server.game; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/src/main/java/de/towerdefence/server/match/GameSession.java b/src/main/java/de/towerdefence/server/game/GameSession.java similarity index 83% rename from src/main/java/de/towerdefence/server/match/GameSession.java rename to src/main/java/de/towerdefence/server/game/GameSession.java index 9bea44c..5e04282 100644 --- a/src/main/java/de/towerdefence/server/match/GameSession.java +++ b/src/main/java/de/towerdefence/server/game/GameSession.java @@ -1,9 +1,8 @@ -package de.towerdefence.server.match; +package de.towerdefence.server.game; -import de.towerdefence.server.match.callbacks.PlayerHitpointsCallback; -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.game.callbacks.PlayerMoneyCallback; +import de.towerdefence.server.game.exeptions.InvalidPlacementException; +import de.towerdefence.server.game.exeptions.InvalidPlacementReason; import de.towerdefence.server.player.Player; import lombok.Getter; diff --git a/src/main/java/de/towerdefence/server/match/Tower.java b/src/main/java/de/towerdefence/server/game/Tower.java similarity index 62% rename from src/main/java/de/towerdefence/server/match/Tower.java rename to src/main/java/de/towerdefence/server/game/Tower.java index ae8b146..48a0c3d 100644 --- a/src/main/java/de/towerdefence/server/match/Tower.java +++ b/src/main/java/de/towerdefence/server/game/Tower.java @@ -1,4 +1,4 @@ -package de.towerdefence.server.match; +package de.towerdefence.server.game; public class Tower { public static final int COST = 20; diff --git a/src/main/java/de/towerdefence/server/match/callbacks/PlayerHitpointsCallback.java b/src/main/java/de/towerdefence/server/game/callbacks/PlayerHitpointsCallback.java similarity index 100% rename from src/main/java/de/towerdefence/server/match/callbacks/PlayerHitpointsCallback.java rename to src/main/java/de/towerdefence/server/game/callbacks/PlayerHitpointsCallback.java diff --git a/src/main/java/de/towerdefence/server/match/callbacks/PlayerMoneyCallback.java b/src/main/java/de/towerdefence/server/game/callbacks/PlayerMoneyCallback.java similarity index 76% rename from src/main/java/de/towerdefence/server/match/callbacks/PlayerMoneyCallback.java rename to src/main/java/de/towerdefence/server/game/callbacks/PlayerMoneyCallback.java index ae35600..ebaacd0 100644 --- a/src/main/java/de/towerdefence/server/match/callbacks/PlayerMoneyCallback.java +++ b/src/main/java/de/towerdefence/server/game/callbacks/PlayerMoneyCallback.java @@ -1,4 +1,4 @@ -package de.towerdefence.server.match.callbacks; +package de.towerdefence.server.game.callbacks; import de.towerdefence.server.player.Player; diff --git a/src/main/java/de/towerdefence/server/match/exeptions/InvalidPlacementException.java b/src/main/java/de/towerdefence/server/game/exeptions/InvalidPlacementException.java similarity index 80% rename from src/main/java/de/towerdefence/server/match/exeptions/InvalidPlacementException.java rename to src/main/java/de/towerdefence/server/game/exeptions/InvalidPlacementException.java index eea167d..9a3727b 100644 --- a/src/main/java/de/towerdefence/server/match/exeptions/InvalidPlacementException.java +++ b/src/main/java/de/towerdefence/server/game/exeptions/InvalidPlacementException.java @@ -1,4 +1,4 @@ -package de.towerdefence.server.match.exeptions; +package de.towerdefence.server.game.exeptions; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/src/main/java/de/towerdefence/server/match/exeptions/InvalidPlacementReason.java b/src/main/java/de/towerdefence/server/game/exeptions/InvalidPlacementReason.java similarity index 86% rename from src/main/java/de/towerdefence/server/match/exeptions/InvalidPlacementReason.java rename to src/main/java/de/towerdefence/server/game/exeptions/InvalidPlacementReason.java index f741f39..17cea2c 100644 --- a/src/main/java/de/towerdefence/server/match/exeptions/InvalidPlacementReason.java +++ b/src/main/java/de/towerdefence/server/game/exeptions/InvalidPlacementReason.java @@ -1,4 +1,4 @@ -package de.towerdefence.server.match.exeptions; +package de.towerdefence.server.game.exeptions; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/src/main/java/de/towerdefence/server/match/Match.java b/src/main/java/de/towerdefence/server/match/Match.java index 959592a..dad8e97 100644 --- a/src/main/java/de/towerdefence/server/match/Match.java +++ b/src/main/java/de/towerdefence/server/match/Match.java @@ -5,8 +5,6 @@ import de.towerdefence.server.match.callbacks.PlayerMoneyCallback; import de.towerdefence.server.player.Player; import lombok.Getter; -import java.util.Optional; - @Getter public class Match { diff --git a/src/main/java/de/towerdefence/server/match/MatchService.java b/src/main/java/de/towerdefence/server/match/MatchService.java index a337e29..31792e4 100644 --- a/src/main/java/de/towerdefence/server/match/MatchService.java +++ b/src/main/java/de/towerdefence/server/match/MatchService.java @@ -11,7 +11,19 @@ import org.springframework.stereotype.Service; import java.util.HashMap; import java.util.Map; import java.util.Optional; -import java.util.concurrent.*; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import org.springframework.stereotype.Service; + +import de.towerdefence.server.game.GameSession; +import de.towerdefence.server.game.Tower; +import de.towerdefence.server.game.callbacks.PlayerMoneyCallback; +import de.towerdefence.server.game.exeptions.InvalidPlacementException; +import de.towerdefence.server.game.exeptions.InvalidPlacementReason; +import de.towerdefence.server.player.Player; @Service public class MatchService { diff --git a/src/main/java/de/towerdefence/server/match/exeptions/NotInMatchException.java b/src/main/java/de/towerdefence/server/match/NotInMatchException.java similarity index 56% rename from src/main/java/de/towerdefence/server/match/exeptions/NotInMatchException.java rename to src/main/java/de/towerdefence/server/match/NotInMatchException.java index 8297e23..e704683 100644 --- a/src/main/java/de/towerdefence/server/match/exeptions/NotInMatchException.java +++ b/src/main/java/de/towerdefence/server/match/NotInMatchException.java @@ -1,4 +1,4 @@ -package de.towerdefence.server.match.exeptions; +package de.towerdefence.server.match; public class NotInMatchException extends RuntimeException { } 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 bc64f77..fa69142 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,9 +1,19 @@ package de.towerdefence.server.server.channels.match; +import java.io.IOException; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; + +import org.springframework.web.socket.CloseStatus; +import org.springframework.web.socket.TextMessage; +import org.springframework.web.socket.WebSocketSession; + import com.fasterxml.jackson.databind.ObjectMapper; + +import de.towerdefence.server.game.exeptions.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.match.NotInMatchException; import de.towerdefence.server.player.Player; import de.towerdefence.server.server.JsonWebsocketHandler; import de.towerdefence.server.server.channels.match.hitpoints.PlayerHitpointsMessage; @@ -14,14 +24,6 @@ import de.towerdefence.server.server.channels.match.placing.RequestTowerPlacingM import de.towerdefence.server.server.channels.match.placing.TowerPlacedMessage; import de.towerdefence.server.session.Channel; import de.towerdefence.server.session.SessionsService; -import org.springframework.web.socket.CloseStatus; -import org.springframework.web.socket.TextMessage; -import org.springframework.web.socket.WebSocketSession; - -import java.io.IOException; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; public class MatchWebsocketHandler extends JsonWebsocketHandler { 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 e96f542..a8b1e95 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 @@ -1,14 +1,14 @@ package de.towerdefence.server.server.channels.match.placing; +import java.util.Map; + import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.JsonNodeFactory; -import de.towerdefence.server.match.exeptions.InvalidPlacementReason; +import de.towerdefence.server.game.exeptions.InvalidPlacementReason; import de.towerdefence.server.server.JsonMessage; import lombok.AllArgsConstructor; -import java.util.Map; - @AllArgsConstructor public class InvalidPlacementMessage extends JsonMessage { -- 2.45.3 From 423fb9ec40183ee545593d7a9c188daed8c2a731 Mon Sep 17 00:00:00 2001 From: Snoweuph Date: Wed, 12 Mar 2025 17:50:23 +0100 Subject: [PATCH 2/4] TD-5: Gegner Wellen Spawning und Nachrichten Struktur --- .forgejo/workflows/qs.yml | 11 +++ build.gradle.kts | 3 + .../de/towerdefence/server/game/Enemy.java | 7 +- .../towerdefence/server/game/GameSession.java | 88 +++++++++++++++++-- .../de/towerdefence/server/game/Tower.java | 8 ++ .../callbacks/EnemiesChangedCallback.java | 9 ++ .../callbacks/PlayerHitpointsCallback.java | 2 +- .../exeptions/WaveInProgressException.java | 4 + .../server/game/pathfinding/AStar.java | 5 ++ .../game/pathfinding/NoPathException.java | 4 + .../de/towerdefence/server/match/Match.java | 21 +++-- .../server/match/MatchService.java | 28 +++--- .../channels/match/MatchWebsocketHandler.java | 38 ++++---- .../match/enemy/EnemyUpdateMessage.java | 61 +++++++++++++ ws/ws.yml | 26 +++++- 15 files changed, 258 insertions(+), 57 deletions(-) create mode 100644 src/main/java/de/towerdefence/server/game/callbacks/EnemiesChangedCallback.java create mode 100644 src/main/java/de/towerdefence/server/game/exeptions/WaveInProgressException.java create mode 100644 src/main/java/de/towerdefence/server/game/pathfinding/AStar.java create mode 100644 src/main/java/de/towerdefence/server/game/pathfinding/NoPathException.java create mode 100644 src/main/java/de/towerdefence/server/server/channels/match/enemy/EnemyUpdateMessage.java diff --git a/.forgejo/workflows/qs.yml b/.forgejo/workflows/qs.yml index 6d48d25..de9bbf4 100644 --- a/.forgejo/workflows/qs.yml +++ b/.forgejo/workflows/qs.yml @@ -28,6 +28,17 @@ jobs: - name: "Stop Gradle" run: gradle --stop + async: + name: "Validate Async API Specification" + runs-on: stable + container: + image: asyncapi/cli:latest + steps: + - name: "Checkout" + uses: "https://git.euph.dev/actions/checkout@v3" + - name: run + run: asyncapi validate ws/ws.yml + linting: name: "Linting" runs-on: stable diff --git a/build.gradle.kts b/build.gradle.kts index ca54b5c..6e67ef7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -61,6 +61,9 @@ dependencies { // Postgres runtimeOnly("org.postgresql:postgresql") + // JOML + implementation("org.joml:joml:1.10.7") + // Lombok compileOnly("org.projectlombok:lombok") annotationProcessor("org.projectlombok:lombok") diff --git a/src/main/java/de/towerdefence/server/game/Enemy.java b/src/main/java/de/towerdefence/server/game/Enemy.java index 3a7828f..dc21088 100644 --- a/src/main/java/de/towerdefence/server/game/Enemy.java +++ b/src/main/java/de/towerdefence/server/game/Enemy.java @@ -2,12 +2,11 @@ package de.towerdefence.server.game; import lombok.AllArgsConstructor; import lombok.Getter; -import lombok.Setter; @Getter -@Setter @AllArgsConstructor public class Enemy { - private int poxX; - private int posY; + protected int id; + protected int x; + protected int y; } diff --git a/src/main/java/de/towerdefence/server/game/GameSession.java b/src/main/java/de/towerdefence/server/game/GameSession.java index 5e04282..fcaee35 100644 --- a/src/main/java/de/towerdefence/server/game/GameSession.java +++ b/src/main/java/de/towerdefence/server/game/GameSession.java @@ -1,18 +1,27 @@ package de.towerdefence.server.game; +import java.lang.reflect.Constructor; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +import de.towerdefence.server.game.callbacks.EnemiesChangedCallback; import de.towerdefence.server.game.callbacks.PlayerMoneyCallback; +import de.towerdefence.server.game.callbacks.PlayerHitpointsCallback; import de.towerdefence.server.game.exeptions.InvalidPlacementException; import de.towerdefence.server.game.exeptions.InvalidPlacementReason; +import de.towerdefence.server.game.exeptions.WaveInProgressException; import de.towerdefence.server.player.Player; import lombok.Getter; +import org.joml.Vector2i; public class GameSession { final static int START_HITPOINTS = 100; 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]; + final static Vector2i MAP_SIZE = new Vector2i(10, 20); + final static int WAVE_SPAWN_DELAY = 200; // ms + final static Vector2i WAVE_SPAWN = new Vector2i(5, 0); + final static Vector2i WAVE_TARGET = new Vector2i(5, 19); private final Player player; @Getter @@ -20,19 +29,77 @@ public class GameSession { @Getter private int playerHitpoints; private final PlayerMoneyCallback moneyCallback; - //private final PlayerHitpointsCallback hitpointsCallback; + private final PlayerHitpointsCallback hitpointsCallback; + private final EnemiesChangedCallback enemyCallback; - public GameSession(Player player, PlayerMoneyCallback moneyCallback) { + private final Tower[][] towers = new Tower[MAP_SIZE.x][MAP_SIZE.y]; + private final Enemy[][] enemies = new Enemy[MAP_SIZE.x][MAP_SIZE.y]; + private List wave = new ArrayList<>(); + private long lastSpawn = Instant.now().toEpochMilli(); + + public GameSession( + Player player, + PlayerMoneyCallback moneyCallback, + PlayerHitpointsCallback hitpointsCallback, + EnemiesChangedCallback enemyCallback) { this.player = player; this.moneyCallback = moneyCallback; - //this.hitpointsCallback = hitpointsCallback; + this.hitpointsCallback = hitpointsCallback; + this.enemyCallback = enemyCallback; this.money = START_MONEY; this.playerHitpoints = START_HITPOINTS; } + public void startWave(List enemies) throws WaveInProgressException { + if (wave.size() > 0) { + throw new WaveInProgressException(); + } + wave = enemies; + } + + public void update() { + List newEnemies = new ArrayList<>(); + + List changedEnemies = moveEnemies(); + + if (wave.size() > 0 && shouldSpawn()) { + enemies[WAVE_SPAWN.x][WAVE_SPAWN.y] = wave.removeFirst(); + newEnemies.add(enemies[WAVE_SPAWN.x][WAVE_SPAWN.y]); + } + + if (newEnemies.size() > 0 || changedEnemies.size() > 0) { + enemyCallback.call( + player, + newEnemies.toArray(new Enemy[newEnemies.size()]), + changedEnemies.toArray(new Enemy[changedEnemies.size()])); + } + } + + private boolean shouldSpawn() { + return lastSpawn + WAVE_SPAWN_DELAY < Instant.now().toEpochMilli() + && enemies[WAVE_SPAWN.x][WAVE_SPAWN.y] == null; + } + + private List moveEnemies() { + // List changedEnemies = moveEnemies(); + + // TODO: Implement Moving of Enemies (possibly through A*) + throw new RuntimeException("NOT IMPLEMENTED"); + + // return changedEnemies; + + } + + /** + * @return the next position to go to + */ + private Vector2i pathfinding(Vector2i start, Vector2i target) { + + } + 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) { + 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) { @@ -41,8 +108,13 @@ public class GameSession { if (money < Tower.COST) { throw new InvalidPlacementException(InvalidPlacementReason.NOT_ENOUGH_MONEY); } + + // TODO: Do Pathfinding check for the placement + money -= Tower.COST; moneyCallback.call(player, money); + tower.x = x; + tower.y = y; towers[x][y] = tower; } diff --git a/src/main/java/de/towerdefence/server/game/Tower.java b/src/main/java/de/towerdefence/server/game/Tower.java index 48a0c3d..a4a9f64 100644 --- a/src/main/java/de/towerdefence/server/game/Tower.java +++ b/src/main/java/de/towerdefence/server/game/Tower.java @@ -1,5 +1,13 @@ package de.towerdefence.server.game; +import lombok.Getter; + +@Getter public class Tower { public static final int COST = 20; + protected int x; + protected int y; + + public Tower() { + } } diff --git a/src/main/java/de/towerdefence/server/game/callbacks/EnemiesChangedCallback.java b/src/main/java/de/towerdefence/server/game/callbacks/EnemiesChangedCallback.java new file mode 100644 index 0000000..06f4a49 --- /dev/null +++ b/src/main/java/de/towerdefence/server/game/callbacks/EnemiesChangedCallback.java @@ -0,0 +1,9 @@ +package de.towerdefence.server.game.callbacks; + +import de.towerdefence.server.game.Enemy; +import de.towerdefence.server.player.Player; + +@FunctionalInterface +public interface EnemiesChangedCallback { + void call(Player player, Enemy[] newEnemies, Enemy[] changedEnemies); +} diff --git a/src/main/java/de/towerdefence/server/game/callbacks/PlayerHitpointsCallback.java b/src/main/java/de/towerdefence/server/game/callbacks/PlayerHitpointsCallback.java index ed1e959..83badd3 100644 --- a/src/main/java/de/towerdefence/server/game/callbacks/PlayerHitpointsCallback.java +++ b/src/main/java/de/towerdefence/server/game/callbacks/PlayerHitpointsCallback.java @@ -1,4 +1,4 @@ -package de.towerdefence.server.match.callbacks; +package de.towerdefence.server.game.callbacks; import de.towerdefence.server.player.Player; diff --git a/src/main/java/de/towerdefence/server/game/exeptions/WaveInProgressException.java b/src/main/java/de/towerdefence/server/game/exeptions/WaveInProgressException.java new file mode 100644 index 0000000..90c8518 --- /dev/null +++ b/src/main/java/de/towerdefence/server/game/exeptions/WaveInProgressException.java @@ -0,0 +1,4 @@ +package de.towerdefence.server.game.exeptions; + +public class WaveInProgressException extends RuntimeException { +} diff --git a/src/main/java/de/towerdefence/server/game/pathfinding/AStar.java b/src/main/java/de/towerdefence/server/game/pathfinding/AStar.java new file mode 100644 index 0000000..d4a4f00 --- /dev/null +++ b/src/main/java/de/towerdefence/server/game/pathfinding/AStar.java @@ -0,0 +1,5 @@ +package de.towerdefence.server.game.pathfinding; + +public class AStar { + +} diff --git a/src/main/java/de/towerdefence/server/game/pathfinding/NoPathException.java b/src/main/java/de/towerdefence/server/game/pathfinding/NoPathException.java new file mode 100644 index 0000000..ce36e57 --- /dev/null +++ b/src/main/java/de/towerdefence/server/game/pathfinding/NoPathException.java @@ -0,0 +1,4 @@ +package de.towerdefence.server.game.pathfinding; + +public class NoPathException extends RuntimeException { +} diff --git a/src/main/java/de/towerdefence/server/match/Match.java b/src/main/java/de/towerdefence/server/match/Match.java index dad8e97..b9cd016 100644 --- a/src/main/java/de/towerdefence/server/match/Match.java +++ b/src/main/java/de/towerdefence/server/match/Match.java @@ -1,7 +1,12 @@ package de.towerdefence.server.match; -import de.towerdefence.server.match.callbacks.PlayerHitpointsCallback; -import de.towerdefence.server.match.callbacks.PlayerMoneyCallback; +import de.towerdefence.server.game.callbacks.PlayerHitpointsCallback; +import java.util.Optional; + +import de.towerdefence.server.game.GameSession; +import de.towerdefence.server.game.callbacks.EnemiesChangedCallback; +import de.towerdefence.server.game.callbacks.PlayerMoneyCallback; +import de.towerdefence.server.game.callbacks.PlayerHitpointsCallback; import de.towerdefence.server.player.Player; import lombok.Getter; @@ -47,21 +52,21 @@ public class Match { } public void connectPlayer( - Player player, - PlayerMoneyCallback moneyCallback, - PlayerHitpointsCallback hitpointsCallback - ) { + Player player, + PlayerMoneyCallback moneyCallback, + PlayerHitpointsCallback hitpointsCallback, + EnemiesChangedCallback enemyCallback) { boolean isPlayer1 = player1.equals(player); boolean isPlayer2 = player2.equals(player); if (!isPlayer1 && !isPlayer2) { return; } if (isPlayer1 && player1Session == null) { - this.player1Session = new GameSession(player, moneyCallback); + this.player1Session = new GameSession(player, moneyCallback, hitpointsCallback, enemyCallback); return; } if (isPlayer2 && player2Session == null) { - this.player2Session = new GameSession(player, moneyCallback); + this.player2Session = new GameSession(player, moneyCallback, hitpointsCallback, enemyCallback); return; } } diff --git a/src/main/java/de/towerdefence/server/match/MatchService.java b/src/main/java/de/towerdefence/server/match/MatchService.java index 31792e4..63729e7 100644 --- a/src/main/java/de/towerdefence/server/match/MatchService.java +++ b/src/main/java/de/towerdefence/server/match/MatchService.java @@ -1,10 +1,9 @@ package de.towerdefence.server.match; -import de.towerdefence.server.match.callbacks.PlayerHitpointsCallback; -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.game.callbacks.PlayerHitpointsCallback; +import de.towerdefence.server.game.callbacks.PlayerMoneyCallback; +import de.towerdefence.server.game.exeptions.InvalidPlacementException; +import de.towerdefence.server.game.exeptions.InvalidPlacementReason; import de.towerdefence.server.player.Player; import org.springframework.stereotype.Service; @@ -16,14 +15,9 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import org.springframework.stereotype.Service; - import de.towerdefence.server.game.GameSession; import de.towerdefence.server.game.Tower; -import de.towerdefence.server.game.callbacks.PlayerMoneyCallback; -import de.towerdefence.server.game.exeptions.InvalidPlacementException; -import de.towerdefence.server.game.exeptions.InvalidPlacementReason; -import de.towerdefence.server.player.Player; +import de.towerdefence.server.game.callbacks.EnemiesChangedCallback; @Service public class MatchService { @@ -62,19 +56,19 @@ public class MatchService { } public void playerConnected( - Player player, - PlayerMoneyCallback moneyCallback, - PlayerHitpointsCallback hitpointsCallback - ) { + Player player, + PlayerMoneyCallback moneyCallback, + PlayerHitpointsCallback hitpointsCallback, + EnemiesChangedCallback enemyCallback) { Match match = playerMatches.get(player); - match.connectPlayer(player, moneyCallback, hitpointsCallback); + match.connectPlayer(player, moneyCallback, hitpointsCallback, enemyCallback); Optional optionalPlayerSession = match.getPlayerGameSession(player); if (optionalPlayerSession.isEmpty()) { return; } GameSession playerSession = optionalPlayerSession.get(); moneyCallback.call(player, playerSession.getMoney()); - hitpointsCallback.call(player, playerSession.getPlayerHitpoints() ); + hitpointsCallback.call(player, playerSession.getPlayerHitpoints()); if (!match.hasMatchStarted()) { return; } 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 fa69142..866b332 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 @@ -11,6 +11,7 @@ import org.springframework.web.socket.WebSocketSession; import com.fasterxml.jackson.databind.ObjectMapper; +import de.towerdefence.server.game.Enemy; import de.towerdefence.server.game.exeptions.InvalidPlacementException; import de.towerdefence.server.match.MatchService; import de.towerdefence.server.match.NotInMatchException; @@ -41,12 +42,20 @@ public class MatchWebsocketHandler extends JsonWebsocketHandler { super.afterConnectionEstablished(session); playerSessions.put(sessionPlayers.get(session), session); matchService.playerConnected( - sessionPlayers.get(session), - this::onPlayerMoneyChanged, - this::onPlayerHitpointsChanged + sessionPlayers.get(session), + this::onPlayerMoneyChanged, + this::onPlayerHitpointsChanged, + this::onEnemiesChanged ); } + @Override + protected void closeSession(WebSocketSession session, CloseStatus reason) { + Player player = sessionPlayers.get(session); + super.closeSession(session, reason); + playerSessions.remove(player); + } + private void onPlayerMoneyChanged(Player player, int playerMoney) { WebSocketSession session = playerSessions.get(player); try { @@ -63,11 +72,8 @@ 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); + private void onEnemiesChanged(Player player, Enemy[] newEnemies, Enemy[] changedEnemies) { + } @Override @@ -75,9 +81,8 @@ public class MatchWebsocketHandler extends JsonWebsocketHandler { try { String payload = message.getPayload(); if (!Objects.equals( - objectMapper.readTree(payload).get("$id").asText(), - RequestTowerPlacingMessage.MESSAGE_ID - )) { + objectMapper.readTree(payload).get("$id").asText(), + RequestTowerPlacingMessage.MESSAGE_ID)) { this.closeSession(session, CloseStatus.BAD_DATA); return; } @@ -89,16 +94,15 @@ public class MatchWebsocketHandler extends JsonWebsocketHandler { } private void handleRequestTowerPlacingMessage( - WebSocketSession session, - String payload - ) throws IOException { + WebSocketSession session, + String payload) throws IOException { RequestTowerPlacingMessage msg = objectMapper.readValue(payload, RequestTowerPlacingMessage.class); Player opponent; try { opponent = this.matchService.placeTower( - this.sessionPlayers.get(session), - msg.getX(), - msg.getY() + this.sessionPlayers.get(session), + msg.getX(), + msg.getY() ); } catch (InvalidPlacementException exception) { new InvalidPlacementMessage(msg.getX(), msg.getY(), exception.getReason()).send(session); diff --git a/src/main/java/de/towerdefence/server/server/channels/match/enemy/EnemyUpdateMessage.java b/src/main/java/de/towerdefence/server/server/channels/match/enemy/EnemyUpdateMessage.java new file mode 100644 index 0000000..c83c359 --- /dev/null +++ b/src/main/java/de/towerdefence/server/server/channels/match/enemy/EnemyUpdateMessage.java @@ -0,0 +1,61 @@ +package de.towerdefence.server.server.channels.match.enemy; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.ObjectNode; + +import de.towerdefence.server.game.Enemy; +import de.towerdefence.server.server.JsonMessage; +import lombok.AllArgsConstructor; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collector; +import java.util.stream.Collectors; + +@AllArgsConstructor +public class EnemyUpdateMessage extends JsonMessage { + + private final Enemy[] newEnemies; + private final Enemy[] changedEnemies; + + @Override + protected String getMessageId() { + return "EnemyUpdate"; + } + + @Override + protected Map getData(JsonNodeFactory factory) { + + List newArray = Arrays + .stream(newEnemies) + .map((Enemy enemy) -> { + ObjectNode o = factory.objectNode(); + o.set("id", factory.numberNode(enemy.getId())); + o.set("x", factory.numberNode(enemy.getX())); + o.set("y", factory.numberNode(enemy.getY())); + return o; + }) + .map(JsonNode.class::cast) + .toList(); + + List changedArray = Arrays + .stream(changedEnemies) + .map((Enemy enemy) -> { + ObjectNode o = factory.objectNode(); + o.set("id", factory.numberNode(enemy.getId())); + o.set("x", factory.numberNode(enemy.getX())); + o.set("y", factory.numberNode(enemy.getY())); + return o; + }) + .map(JsonNode.class::cast) + .toList(); + + return Map.of( + "new", new ArrayNode(factory, newArray), + "changed", new ArrayNode(factory, changedArray) + ); + } +} diff --git a/ws/ws.yml b/ws/ws.yml index 2ac309b..d512df7 100644 --- a/ws/ws.yml +++ b/ws/ws.yml @@ -253,8 +253,23 @@ channels: required: - $id - playerHitpoints - - + EnemyUpdate: + description: All Information needed to updated the Enemy Information in Game + payload: + type: object + additionalProperties: false + properties: + $id: + type: string + format: messageId + new: + type: array + changed: + type: array + required: + - $id + - new + - changed operations: requestConnectionToken: @@ -343,6 +358,13 @@ operations: $ref: "#/channels/match" messages: - $ref: "#/channels/match/messages/PlayerHitpoints" + enemyUpdate: + title: EnemyUpdate + action: receive + channel: + $ref: "#/channels/match" + messages: + - $ref: "#/channels/match/messages/EnemyUpdate" -- 2.45.3 From ac434b84632250687e0e66ddd960c229742492b7 Mon Sep 17 00:00:00 2001 From: Snoweuph Date: Thu, 13 Mar 2025 17:42:00 +0100 Subject: [PATCH 3/4] TD-5: A* Implementation --- .../de/towerdefence/server/game/Enemy.java | 5 +- .../towerdefence/server/game/GameSession.java | 44 ++++++--- .../server/game/pathfinding/AStar.java | 97 ++++++++++++++++++- .../pathfinding/OutOfBoundsException.java | 4 + .../server/game/pathfinding/PathNode.java | 12 +++ .../game/pathfinding/WalkableCallback.java | 8 ++ 6 files changed, 155 insertions(+), 15 deletions(-) create mode 100644 src/main/java/de/towerdefence/server/game/pathfinding/OutOfBoundsException.java create mode 100644 src/main/java/de/towerdefence/server/game/pathfinding/PathNode.java create mode 100644 src/main/java/de/towerdefence/server/game/pathfinding/WalkableCallback.java diff --git a/src/main/java/de/towerdefence/server/game/Enemy.java b/src/main/java/de/towerdefence/server/game/Enemy.java index dc21088..cbd0112 100644 --- a/src/main/java/de/towerdefence/server/game/Enemy.java +++ b/src/main/java/de/towerdefence/server/game/Enemy.java @@ -1,5 +1,7 @@ package de.towerdefence.server.game; +import org.joml.Vector2i; + import lombok.AllArgsConstructor; import lombok.Getter; @@ -7,6 +9,5 @@ import lombok.Getter; @AllArgsConstructor public class Enemy { protected int id; - protected int x; - protected int y; + protected Vector2i pos; } diff --git a/src/main/java/de/towerdefence/server/game/GameSession.java b/src/main/java/de/towerdefence/server/game/GameSession.java index fcaee35..a31e6ad 100644 --- a/src/main/java/de/towerdefence/server/game/GameSession.java +++ b/src/main/java/de/towerdefence/server/game/GameSession.java @@ -11,6 +11,9 @@ import de.towerdefence.server.game.callbacks.PlayerHitpointsCallback; import de.towerdefence.server.game.exeptions.InvalidPlacementException; import de.towerdefence.server.game.exeptions.InvalidPlacementReason; import de.towerdefence.server.game.exeptions.WaveInProgressException; +import de.towerdefence.server.game.pathfinding.AStar; +import de.towerdefence.server.game.pathfinding.NoPathException; +import de.towerdefence.server.game.pathfinding.OutOfBoundsException; import de.towerdefence.server.player.Player; import lombok.Getter; import org.joml.Vector2i; @@ -82,20 +85,37 @@ public class GameSession { } private List moveEnemies() { - // List changedEnemies = moveEnemies(); + List changedEnemies = moveEnemies(); - // TODO: Implement Moving of Enemies (possibly through A*) - throw new RuntimeException("NOT IMPLEMENTED"); - - // return changedEnemies; - - } - - /** - * @return the next position to go to - */ - private Vector2i pathfinding(Vector2i start, Vector2i target) { + for (int x = 0; x < enemies.length; x++) { + Enemy[] column = enemies[x]; + for (int y = 0; y < column.length; y++) { + Enemy enemy = column[y]; + if (changedEnemies.contains(enemy)) { + continue; + } + AStar a = new AStar((Vector2i pos) -> { + return pos.x >= 0 + && pos.x < MAP_SIZE.x + && pos.y >= 0 + && pos.y < MAP_SIZE.y + && (towers[pos.x] == null || towers[pos.x][pos.y] == null) + && (enemies[pos.x] == null || enemies[pos.x][pos.y] != null); + }); + Vector2i next; + try { + next = a.next(enemy.pos, WAVE_TARGET); + } catch (NoPathException | OutOfBoundsException exceptionO) { + throw new RuntimeException("TODO: Implement"); + } + enemy.pos = next; + enemies[x][y] = null; + enemies[next.x][next.y] = enemy; + changedEnemies.add(enemy); + } + } + return changedEnemies; } public void placeTower(Tower tower, int x, int y) throws InvalidPlacementException { diff --git a/src/main/java/de/towerdefence/server/game/pathfinding/AStar.java b/src/main/java/de/towerdefence/server/game/pathfinding/AStar.java index d4a4f00..28c37b5 100644 --- a/src/main/java/de/towerdefence/server/game/pathfinding/AStar.java +++ b/src/main/java/de/towerdefence/server/game/pathfinding/AStar.java @@ -1,5 +1,100 @@ package de.towerdefence.server.game.pathfinding; -public class AStar { +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.joml.Vector2i; + +import lombok.AllArgsConstructor; + +@AllArgsConstructor +public class AStar { + private final WalkableCallback walkable; + + public Vector2i next(Vector2i current, Vector2i target) throws NoPathException, OutOfBoundsException { + if (!walkable.call(current) || !walkable.call(target)) { + throw new OutOfBoundsException(); + } + + if (current.equals(target)) { + return target; + } + + Map remaining = new HashMap<>(); + Set evaluated = new HashSet<>(); + Map takenPath = new HashMap<>(); + + PathNode origin = new PathNode(current, 0, current.gridDistance(target)); + remaining.put(current, origin); + + while (!remaining.isEmpty()) { + PathNode currentNode = getLowestEstimate(remaining); + Vector2i currentPos = currentNode.pos; + + if (currentPos.equals(target)) { + return reconstructPath(takenPath, current); + } + + remaining.remove(currentPos); + evaluated.add(currentPos); + + long nextCost = currentNode.cost + 1; + for (PathNode neighbor : getNeighbors(currentPos, nextCost, target)) { + if (evaluated.contains(neighbor.pos)) { + continue; + } + if (!remaining.containsKey(neighbor.pos) || nextCost < remaining.get(neighbor.pos).cost) { + takenPath.put(neighbor.pos, currentPos); + remaining.put(neighbor.pos, neighbor); + } + } + } + + throw new NoPathException(); + } + + private Vector2i reconstructPath(Map takenPath, Vector2i origin) { + Vector2i current = takenPath.keySet().stream().reduce((first, second) -> second).orElse(null); + if (current == null) + return null; + while (takenPath.get(current) != null && !takenPath.get(current).equals(origin)) { + current = takenPath.get(current); + } + return current; + } + + private PathNode getLowestEstimate(Map openSet) { + PathNode lowestNode = null; + for (PathNode node : openSet.values()) { + if (lowestNode == null || node.estimate < lowestNode.estimate) { + lowestNode = node; + } + } + return lowestNode; + } + + private List getNeighbors(Vector2i pos, long nextCost, Vector2i target) { + List neighbors = new ArrayList<>(); + + addNeighbor(neighbors, new Vector2i(pos.x + 1, pos.y), nextCost, target); + addNeighbor(neighbors, new Vector2i(pos.x - 1, pos.y), nextCost, target); + addNeighbor(neighbors, new Vector2i(pos.x, pos.y + 1), nextCost, target); + addNeighbor(neighbors, new Vector2i(pos.x, pos.y - 1), nextCost, target); + return neighbors; + } + + private void addNeighbor(List accumulator, Vector2i pos, long nextCost, Vector2i target) { + Vector2i right = new Vector2i(pos.x + 1, pos.y); + if (!walkable.call(pos)) { + return; + } + accumulator.add(new PathNode( + right, + nextCost, + right.gridDistance(target) + nextCost)); + } } diff --git a/src/main/java/de/towerdefence/server/game/pathfinding/OutOfBoundsException.java b/src/main/java/de/towerdefence/server/game/pathfinding/OutOfBoundsException.java new file mode 100644 index 0000000..3b77118 --- /dev/null +++ b/src/main/java/de/towerdefence/server/game/pathfinding/OutOfBoundsException.java @@ -0,0 +1,4 @@ +package de.towerdefence.server.game.pathfinding; + +public class OutOfBoundsException extends RuntimeException { +} diff --git a/src/main/java/de/towerdefence/server/game/pathfinding/PathNode.java b/src/main/java/de/towerdefence/server/game/pathfinding/PathNode.java new file mode 100644 index 0000000..76893c0 --- /dev/null +++ b/src/main/java/de/towerdefence/server/game/pathfinding/PathNode.java @@ -0,0 +1,12 @@ +package de.towerdefence.server.game.pathfinding; + +import org.joml.Vector2i; + +import lombok.AllArgsConstructor; + +@AllArgsConstructor +final class PathNode { + protected final Vector2i pos; + protected final long cost; // This is the cost to this Node + protected final long estimate; // This is the estimated remaining cost to the Target +} diff --git a/src/main/java/de/towerdefence/server/game/pathfinding/WalkableCallback.java b/src/main/java/de/towerdefence/server/game/pathfinding/WalkableCallback.java new file mode 100644 index 0000000..90c737c --- /dev/null +++ b/src/main/java/de/towerdefence/server/game/pathfinding/WalkableCallback.java @@ -0,0 +1,8 @@ +package de.towerdefence.server.game.pathfinding; + +import org.joml.Vector2i; + +@FunctionalInterface +public interface WalkableCallback { + boolean call(Vector2i pos); +} -- 2.45.3 From eff9ea139abcc5f06893e26f7699fb68e322969e Mon Sep 17 00:00:00 2001 From: Snoweuph Date: Thu, 13 Mar 2025 19:17:24 +0100 Subject: [PATCH 4/4] TD-5: Cleanup and Finalizing --- .../towerdefence/server/game/GameSession.java | 39 +++++++------- .../de/towerdefence/server/game/Tower.java | 5 +- .../server/game/pathfinding/AStar.java | 7 ++- .../server/game/pathfinding/PathNode.java | 6 +-- .../de/towerdefence/server/match/Match.java | 9 ++-- .../server/match/MatchService.java | 27 +++++----- .../channels/match/MatchWebsocketHandler.java | 54 ++++++++++--------- .../match/enemy/EnemyUpdateMessage.java | 10 ++-- .../placing/InvalidPlacementMessage.java | 7 +-- .../match/placing/TowerPlacedMessage.java | 8 +-- src/main/resources/spotbugs-exclude.xml | 6 +++ 11 files changed, 97 insertions(+), 81 deletions(-) diff --git a/src/main/java/de/towerdefence/server/game/GameSession.java b/src/main/java/de/towerdefence/server/game/GameSession.java index a31e6ad..ec5f976 100644 --- a/src/main/java/de/towerdefence/server/game/GameSession.java +++ b/src/main/java/de/towerdefence/server/game/GameSession.java @@ -41,10 +41,11 @@ public class GameSession { private long lastSpawn = Instant.now().toEpochMilli(); public GameSession( - Player player, - PlayerMoneyCallback moneyCallback, - PlayerHitpointsCallback hitpointsCallback, - EnemiesChangedCallback enemyCallback) { + Player player, + PlayerMoneyCallback moneyCallback, + PlayerHitpointsCallback hitpointsCallback, + EnemiesChangedCallback enemyCallback + ) { this.player = player; this.moneyCallback = moneyCallback; this.hitpointsCallback = hitpointsCallback; @@ -73,15 +74,16 @@ public class GameSession { if (newEnemies.size() > 0 || changedEnemies.size() > 0) { enemyCallback.call( - player, - newEnemies.toArray(new Enemy[newEnemies.size()]), - changedEnemies.toArray(new Enemy[changedEnemies.size()])); + player, + newEnemies.toArray(new Enemy[newEnemies.size()]), + changedEnemies.toArray(new Enemy[changedEnemies.size()]) + ); } } private boolean shouldSpawn() { return lastSpawn + WAVE_SPAWN_DELAY < Instant.now().toEpochMilli() - && enemies[WAVE_SPAWN.x][WAVE_SPAWN.y] == null; + && enemies[WAVE_SPAWN.x][WAVE_SPAWN.y] == null; } private List moveEnemies() { @@ -96,11 +98,11 @@ public class GameSession { } AStar a = new AStar((Vector2i pos) -> { return pos.x >= 0 - && pos.x < MAP_SIZE.x - && pos.y >= 0 - && pos.y < MAP_SIZE.y - && (towers[pos.x] == null || towers[pos.x][pos.y] == null) - && (enemies[pos.x] == null || enemies[pos.x][pos.y] != null); + && pos.x < MAP_SIZE.x + && pos.y >= 0 + && pos.y < MAP_SIZE.y + && (towers[pos.x] == null || towers[pos.x][pos.y] == null) + && (enemies[pos.x] == null || enemies[pos.x][pos.y] != null); }); Vector2i next; try { @@ -118,11 +120,11 @@ public class GameSession { return changedEnemies; } - 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) { + public void placeTower(Tower tower, Vector2i pos) throws InvalidPlacementException { + if (pos.x < 0 || pos.y < 0 || pos.x + 1 > MAP_SIZE.x || pos.y + 1 > MAP_SIZE.y) { throw new InvalidPlacementException(InvalidPlacementReason.OUT_OF_BOUNDS); } - if (towers[x][y] != null) { + if (towers[pos.x][pos.y] != null) { throw new InvalidPlacementException(InvalidPlacementReason.LOCATION_USED); } if (money < Tower.COST) { @@ -133,9 +135,8 @@ public class GameSession { money -= Tower.COST; moneyCallback.call(player, money); - tower.x = x; - tower.y = y; - towers[x][y] = tower; + tower.pos = pos; + towers[pos.x][pos.y] = tower; } public void addMoney(int amount) { diff --git a/src/main/java/de/towerdefence/server/game/Tower.java b/src/main/java/de/towerdefence/server/game/Tower.java index a4a9f64..4c30055 100644 --- a/src/main/java/de/towerdefence/server/game/Tower.java +++ b/src/main/java/de/towerdefence/server/game/Tower.java @@ -1,12 +1,13 @@ package de.towerdefence.server.game; +import org.joml.Vector2i; + import lombok.Getter; @Getter public class Tower { public static final int COST = 20; - protected int x; - protected int y; + protected Vector2i pos; public Tower() { } diff --git a/src/main/java/de/towerdefence/server/game/pathfinding/AStar.java b/src/main/java/de/towerdefence/server/game/pathfinding/AStar.java index 28c37b5..00cbbdd 100644 --- a/src/main/java/de/towerdefence/server/game/pathfinding/AStar.java +++ b/src/main/java/de/towerdefence/server/game/pathfinding/AStar.java @@ -92,9 +92,12 @@ public class AStar { if (!walkable.call(pos)) { return; } - accumulator.add(new PathNode( + accumulator.add( + new PathNode( right, nextCost, - right.gridDistance(target) + nextCost)); + right.gridDistance(target) + nextCost + ) + ); } } diff --git a/src/main/java/de/towerdefence/server/game/pathfinding/PathNode.java b/src/main/java/de/towerdefence/server/game/pathfinding/PathNode.java index 76893c0..f7e951d 100644 --- a/src/main/java/de/towerdefence/server/game/pathfinding/PathNode.java +++ b/src/main/java/de/towerdefence/server/game/pathfinding/PathNode.java @@ -6,7 +6,7 @@ import lombok.AllArgsConstructor; @AllArgsConstructor final class PathNode { - protected final Vector2i pos; - protected final long cost; // This is the cost to this Node - protected final long estimate; // This is the estimated remaining cost to the Target + final Vector2i pos; + final long cost; // This is the cost to this Node + final long estimate; // This is the estimated remaining cost to the Target } diff --git a/src/main/java/de/towerdefence/server/match/Match.java b/src/main/java/de/towerdefence/server/match/Match.java index b9cd016..43655ed 100644 --- a/src/main/java/de/towerdefence/server/match/Match.java +++ b/src/main/java/de/towerdefence/server/match/Match.java @@ -52,10 +52,11 @@ public class Match { } public void connectPlayer( - Player player, - PlayerMoneyCallback moneyCallback, - PlayerHitpointsCallback hitpointsCallback, - EnemiesChangedCallback enemyCallback) { + Player player, + PlayerMoneyCallback moneyCallback, + PlayerHitpointsCallback hitpointsCallback, + EnemiesChangedCallback enemyCallback + ) { boolean isPlayer1 = player1.equals(player); boolean isPlayer2 = player2.equals(player); if (!isPlayer1 && !isPlayer2) { diff --git a/src/main/java/de/towerdefence/server/match/MatchService.java b/src/main/java/de/towerdefence/server/match/MatchService.java index 63729e7..306eaae 100644 --- a/src/main/java/de/towerdefence/server/match/MatchService.java +++ b/src/main/java/de/towerdefence/server/match/MatchService.java @@ -5,6 +5,8 @@ import de.towerdefence.server.game.callbacks.PlayerMoneyCallback; import de.towerdefence.server.game.exeptions.InvalidPlacementException; import de.towerdefence.server.game.exeptions.InvalidPlacementReason; import de.towerdefence.server.player.Player; + +import org.joml.Vector2i; import org.springframework.stereotype.Service; import java.util.HashMap; @@ -51,15 +53,16 @@ public class MatchService { if (opponent.isEmpty()) { throw new NotInMatchException(); } - playerSession.get().placeTower(new Tower(), x, y); + playerSession.get().placeTower(new Tower(), new Vector2i(x, y)); return opponent.get(); } public void playerConnected( - Player player, - PlayerMoneyCallback moneyCallback, - PlayerHitpointsCallback hitpointsCallback, - EnemiesChangedCallback enemyCallback) { + Player player, + PlayerMoneyCallback moneyCallback, + PlayerHitpointsCallback hitpointsCallback, + EnemiesChangedCallback enemyCallback + ) { Match match = playerMatches.get(player); match.connectPlayer(player, moneyCallback, hitpointsCallback, enemyCallback); Optional optionalPlayerSession = match.getPlayerGameSession(player); @@ -83,13 +86,13 @@ public class MatchService { } GameSession opponentSession = optionalOpponentSession.get(); ScheduledFuture moneyTask = scheduler.scheduleAtFixedRate( - () -> { - playerSession.addMoney(3); - opponentSession.addMoney(3); - }, - 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/server/channels/match/MatchWebsocketHandler.java b/src/main/java/de/towerdefence/server/server/channels/match/MatchWebsocketHandler.java index 866b332..a155f33 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,22 +1,13 @@ package de.towerdefence.server.server.channels.match; -import java.io.IOException; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; - -import org.springframework.web.socket.CloseStatus; -import org.springframework.web.socket.TextMessage; -import org.springframework.web.socket.WebSocketSession; - import com.fasterxml.jackson.databind.ObjectMapper; - import de.towerdefence.server.game.Enemy; import de.towerdefence.server.game.exeptions.InvalidPlacementException; import de.towerdefence.server.match.MatchService; import de.towerdefence.server.match.NotInMatchException; import de.towerdefence.server.player.Player; import de.towerdefence.server.server.JsonWebsocketHandler; +import de.towerdefence.server.server.channels.match.enemy.EnemyUpdateMessage; import de.towerdefence.server.server.channels.match.hitpoints.PlayerHitpointsMessage; import de.towerdefence.server.server.channels.match.money.PlayerMoneyMessage; import de.towerdefence.server.server.channels.match.placing.GamePlayerMap; @@ -25,6 +16,13 @@ import de.towerdefence.server.server.channels.match.placing.RequestTowerPlacingM import de.towerdefence.server.server.channels.match.placing.TowerPlacedMessage; import de.towerdefence.server.session.Channel; import de.towerdefence.server.session.SessionsService; +import java.io.IOException; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import org.springframework.web.socket.CloseStatus; +import org.springframework.web.socket.TextMessage; +import org.springframework.web.socket.WebSocketSession; public class MatchWebsocketHandler extends JsonWebsocketHandler { @@ -42,10 +40,10 @@ public class MatchWebsocketHandler extends JsonWebsocketHandler { super.afterConnectionEstablished(session); playerSessions.put(sessionPlayers.get(session), session); matchService.playerConnected( - sessionPlayers.get(session), - this::onPlayerMoneyChanged, - this::onPlayerHitpointsChanged, - this::onEnemiesChanged + sessionPlayers.get(session), + this::onPlayerMoneyChanged, + this::onPlayerHitpointsChanged, + this::onEnemiesChanged ); } @@ -73,7 +71,11 @@ public class MatchWebsocketHandler extends JsonWebsocketHandler { } private void onEnemiesChanged(Player player, Enemy[] newEnemies, Enemy[] changedEnemies) { - + WebSocketSession session = playerSessions.get(player); + try { + new EnemyUpdateMessage(newEnemies, changedEnemies).send(session); + } catch (IOException ignored) { + } } @Override @@ -81,8 +83,9 @@ public class MatchWebsocketHandler extends JsonWebsocketHandler { try { String payload = message.getPayload(); if (!Objects.equals( - objectMapper.readTree(payload).get("$id").asText(), - RequestTowerPlacingMessage.MESSAGE_ID)) { + objectMapper.readTree(payload).get("$id").asText(), + RequestTowerPlacingMessage.MESSAGE_ID + )) { this.closeSession(session, CloseStatus.BAD_DATA); return; } @@ -90,19 +93,18 @@ public class MatchWebsocketHandler extends JsonWebsocketHandler { } catch (IOException ignored) { this.closeSession(session, CloseStatus.BAD_DATA); } - } - private void handleRequestTowerPlacingMessage( - WebSocketSession session, - String payload) throws IOException { - RequestTowerPlacingMessage msg = objectMapper.readValue(payload, RequestTowerPlacingMessage.class); + private void handleRequestTowerPlacingMessage(WebSocketSession session, String payload) + throws IOException { + RequestTowerPlacingMessage msg = + objectMapper.readValue(payload, RequestTowerPlacingMessage.class); Player opponent; try { - opponent = this.matchService.placeTower( - this.sessionPlayers.get(session), - msg.getX(), - msg.getY() + opponent =this.matchService.placeTower( + this.sessionPlayers.get(session), + msg.getX(), + msg.getY() ); } catch (InvalidPlacementException exception) { new InvalidPlacementMessage(msg.getX(), msg.getY(), exception.getReason()).send(session); diff --git a/src/main/java/de/towerdefence/server/server/channels/match/enemy/EnemyUpdateMessage.java b/src/main/java/de/towerdefence/server/server/channels/match/enemy/EnemyUpdateMessage.java index c83c359..909e4f7 100644 --- a/src/main/java/de/towerdefence/server/server/channels/match/enemy/EnemyUpdateMessage.java +++ b/src/main/java/de/towerdefence/server/server/channels/match/enemy/EnemyUpdateMessage.java @@ -12,8 +12,6 @@ import lombok.AllArgsConstructor; import java.util.Arrays; import java.util.List; import java.util.Map; -import java.util.stream.Collector; -import java.util.stream.Collectors; @AllArgsConstructor public class EnemyUpdateMessage extends JsonMessage { @@ -34,8 +32,8 @@ public class EnemyUpdateMessage extends JsonMessage { .map((Enemy enemy) -> { ObjectNode o = factory.objectNode(); o.set("id", factory.numberNode(enemy.getId())); - o.set("x", factory.numberNode(enemy.getX())); - o.set("y", factory.numberNode(enemy.getY())); + o.set("x", factory.numberNode(enemy.getPos().x)); + o.set("y", factory.numberNode(enemy.getPos().y)); return o; }) .map(JsonNode.class::cast) @@ -46,8 +44,8 @@ public class EnemyUpdateMessage extends JsonMessage { .map((Enemy enemy) -> { ObjectNode o = factory.objectNode(); o.set("id", factory.numberNode(enemy.getId())); - o.set("x", factory.numberNode(enemy.getX())); - o.set("y", factory.numberNode(enemy.getY())); + o.set("x", factory.numberNode(enemy.getPos().x)); + o.set("y", factory.numberNode(enemy.getPos().y)); return o; }) .map(JsonNode.class::cast) 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 a8b1e95..0597fa8 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 @@ -24,8 +24,9 @@ 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/TowerPlacedMessage.java b/src/main/java/de/towerdefence/server/server/channels/match/placing/TowerPlacedMessage.java index fcf3a31..ed5be87 100644 --- a/src/main/java/de/towerdefence/server/server/channels/match/placing/TowerPlacedMessage.java +++ b/src/main/java/de/towerdefence/server/server/channels/match/placing/TowerPlacedMessage.java @@ -22,9 +22,9 @@ public class TowerPlacedMessage extends JsonMessage { @Override protected Map getData(JsonNodeFactory factory) { return Map.of( - "x", factory.numberNode(this.x), - "y", factory.numberNode(this.y), - "map", factory.textNode(this.map.getJsonName()) - ); + "x", factory.numberNode(this.x), + "y", factory.numberNode(this.y), + "map", factory.textNode(this.map.getJsonName()) + ); } } diff --git a/src/main/resources/spotbugs-exclude.xml b/src/main/resources/spotbugs-exclude.xml index d737fbf..d3a00dc 100644 --- a/src/main/resources/spotbugs-exclude.xml +++ b/src/main/resources/spotbugs-exclude.xml @@ -25,4 +25,10 @@ + + + + + + -- 2.45.3