Merge pull request '[Chore]: Reafactoring Match daten' (!14) from chore/refactoring-match into trunk
All checks were successful
Quality Check / Validate OAS (push) Successful in 37s
Build Application / build (push) Successful in 1m7s
Quality Check / Linting (push) Successful in 1m10s
Quality Check / Testing (push) Successful in 1m12s
Quality Check / Static Analysis (push) Successful in 1m16s
Build Application / build-docker (push) Successful in 19s
Build Application / release (push) Successful in 10s

Reviewed-on: #14
Reviewed-by: SZUT-Kevin <kevin.schmidt9101@gmail.com>
This commit is contained in:
Snoweuph 2025-03-12 13:40:23 +00:00 committed by Euph Forge
commit bc636df0f8
Signed by: Euph Forge
GPG key ID: 85A06461FB6BDBB7
16 changed files with 204 additions and 136 deletions

View file

@ -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;
}

View file

@ -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;
}
}

View file

@ -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<Player> 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<Integer> 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<GameSession> 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;
}
}

View file

@ -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 void playerConnected(Player player, PlayerMoneyCallback callback) {
public Player placeTower(Player player, int x, int y) throws NotInMatchException, InvalidPlacementException {
Match match = playerMatches.get(player);
Optional<Integer> currentMoney = match.getPlayerMoney(player);
if (currentMoney.isEmpty()) {
if (!match.hasMatchStarted()) {
throw new InvalidPlacementException(InvalidPlacementReason.MATCH_NOT_STARTED);
}
Optional<GameSession> playerSession = match.getPlayerGameSession(player);
if (playerSession.isEmpty()) {
throw new NotInMatchException();
}
Optional<Player> 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 moneyCallback) {
Match match = playerMatches.get(player);
match.connectPlayer(player, moneyCallback);
Optional<GameSession> 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<Player> optionalOpponent = match.getOpponent(player);
if (optionalOpponent.isEmpty()) {
return;
}
Player opponent = optionalOpponent.get();
Optional<GameSession> 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());
playerSession.addMoney(3);
opponentSession.addMoney(3);
},
5,
5,
TimeUnit.SECONDS
);
TimeUnit.SECONDS);
moneyTasks.put(match, moneyTask);
}
}

View file

@ -0,0 +1,5 @@
package de.towerdefence.server.match;
public class Tower {
public static final int COST = 20;
}

View file

@ -1,4 +1,4 @@
package de.towerdefence.server.server.channels.match.money;
package de.towerdefence.server.match.callbacks;
import de.towerdefence.server.player.Player;

View file

@ -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;

View file

@ -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");

View file

@ -0,0 +1,4 @@
package de.towerdefence.server.match.exeptions;
public class NotInMatchException extends RuntimeException {
}

View file

@ -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);

View file

@ -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);

View file

@ -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<String, JsonNode> getData(JsonNodeFactory factory) {
return Map.of(
"playerMoney", factory.numberNode(this.playerMoney)
);
}
}

View file

@ -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;
@ -24,7 +26,6 @@ public class InvalidPlacementMessage extends JsonMessage {
return Map.of(
"x", factory.numberNode(this.x),
"y", factory.numberNode(this.y),
"reason", factory.textNode(this.reason.getJsonName())
);
"reason", factory.textNode(this.reason.getJsonName()));
}
}

View file

@ -1,4 +0,0 @@
package de.towerdefence.server.server.channels.match.placing;
public class Tower {
}

View file

@ -19,4 +19,10 @@
<Class name="de.towerdefence.server.match.confirmation.MatchConfirmationService"/>
<Bug code="M,D,SF"/>
</Match>
<Match>
<!-- This is Only a Placeholder Object, thus it's not a bad practice to instance it-->
<Class name="de.towerdefence.server.match.MatchService"/>
<Method name="placeTower"/>
<Bug code="L,B,ISC"/>
</Match>
</FindBugsFilter>

View file

@ -216,6 +216,7 @@ channels:
reason:
type: string
enum:
- "match-not-started"
- "out-of-bounds"
- "location-used"
- "not-enough-money"