diff --git a/Projekte/battleship/model/src/main/java/pp/battleship/game/client/EditorState.java b/Projekte/battleship/model/src/main/java/pp/battleship/game/client/EditorState.java index 3e5b837e..fa45fd75 100644 --- a/Projekte/battleship/model/src/main/java/pp/battleship/game/client/EditorState.java +++ b/Projekte/battleship/model/src/main/java/pp/battleship/game/client/EditorState.java @@ -10,12 +10,14 @@ import pp.battleship.message.client.MapMessage; import pp.battleship.model.Battleship; import pp.battleship.model.IntPoint; +import pp.battleship.model.Rotation; import pp.battleship.model.ShipMap; import pp.battleship.model.dto.ShipMapDTO; import java.io.File; import java.io.IOException; import java.lang.System.Logger.Level; +import java.util.List; import static pp.battleship.Resources.lookup; import static pp.battleship.model.Battleship.Status.INVALID_PREVIEW; @@ -238,6 +240,8 @@ public void loadMap(File file) throws IOException { final ShipMapDTO dto = ShipMapDTO.loadFrom(file); if (!dto.fits(logic.getDetails())) throw new IOException(lookup("map.doesnt.fit")); + if (!validMap(dto)) + throw new IOException(lookup("map.invalid")); ownMap().clear(); dto.getShips().forEach(ownMap()::add); harbor().clear(); @@ -264,4 +268,70 @@ public boolean mayLoadMap() { public boolean maySaveMap() { return harbor().getItems().isEmpty(); } + + /** + * Validates the given ShipMapDTO by checking if all ships are within bounds + * and do not overlap with each other. + * + * @param dto the ShipMapDTO to validate + * @return true if the map is valid, false otherwise + */ + private boolean validMap(ShipMapDTO dto) { + return inBounds(dto) && !overlaps(dto); + } + + /** + * Checks if all ships in the given ShipMapDTO are within the bounds of the map. + * + * @param dto the ShipMapDTO to validate + * @return true if all ships are within bounds, false otherwise + */ + private boolean inBounds(ShipMapDTO dto) { + List ships = dto.getShips(); + for (Battleship ship : ships) { + if (!isWithinBounds(ship, dto.getWidth(), dto.getHeight())) { + return false; + } + } + return true; + } + + /** + * Checks if the given ship is within the bounds of the map. + * + * @param ship the Battleship to check + * @param width the width of the map + * @param height the height of the map + * @return true if the ship is within bounds, false otherwise + */ + private boolean isWithinBounds(Battleship ship, int width, int height) { + int x1 = ship.getX(); + int y1 = ship.getY(); + Rotation r = ship.getRot(); + int x2 = x1 + ship.getLength() * r.dx(); + int y2 = y1 + ship.getLength() * r.dy(); + + return x1 >= 0 && y1 >= 0 && x1 <= width && y1 <= height && + x2 >= 0 && y2 >= 0 && x2 <= width && y2 <= height; + } + + /** + * Checks if any ships in the given ShipMapDTO overlap with each other. + * + * @param dto the ShipMapDTO to validate + * @return true if any ships overlap, false otherwise + */ + private boolean overlaps(ShipMapDTO dto) { + List ships = dto.getShips(); + for (int i = 0; i < ships.size(); i++) { + Battleship ship1 = ships.get(i); + for (int j = i + 1; j < ships.size(); j++) { + Battleship ship2 = ships.get(j); + if (ship1.collidesWith(ship2)) { + return true; // Collision detected + } + } + } + return false; + } } diff --git a/Projekte/battleship/model/src/main/java/pp/battleship/game/client/WaitState.java b/Projekte/battleship/model/src/main/java/pp/battleship/game/client/WaitState.java index 90cb8176..13d8fc80 100644 --- a/Projekte/battleship/model/src/main/java/pp/battleship/game/client/WaitState.java +++ b/Projekte/battleship/model/src/main/java/pp/battleship/game/client/WaitState.java @@ -7,6 +7,7 @@ package pp.battleship.game.client; +import pp.battleship.message.server.GameDetails; import pp.battleship.message.server.StartBattleMessage; import java.lang.System.Logger.Level; @@ -38,4 +39,17 @@ public void receivedStartBattle(StartBattleMessage msg) { logic.setInfoText(msg.getInfoTextKey()); logic.setState(new BattleState(logic, msg.isMyTurn())); } + + /** + * Handles the GameDetails message received from the server. + * If the map is invalid, the editor state is set. + * + * @param msg the GameDetails message received + */ + @Override + public void receivedGameDetails(GameDetails msg) { + ClientGameLogic.LOGGER.log(Level.WARNING, "Invalid Map"); //NON-NLS + logic.setInfoText("map.invalid"); + logic.setState(new EditorState(logic)); + } } diff --git a/Projekte/battleship/model/src/main/java/pp/battleship/game/server/ServerGameLogic.java b/Projekte/battleship/model/src/main/java/pp/battleship/game/server/ServerGameLogic.java index a5987fa1..b0e07049 100644 --- a/Projekte/battleship/model/src/main/java/pp/battleship/game/server/ServerGameLogic.java +++ b/Projekte/battleship/model/src/main/java/pp/battleship/game/server/ServerGameLogic.java @@ -17,6 +17,7 @@ import pp.battleship.message.server.StartBattleMessage; import pp.battleship.model.Battleship; import pp.battleship.model.IntPoint; +import pp.battleship.model.Rotation; import java.lang.System.Logger; import java.lang.System.Logger.Level; @@ -142,8 +143,75 @@ public Player addPlayer(int id) { public void received(MapMessage msg, int from) { if (state != ServerState.SET_UP) LOGGER.log(Level.ERROR, "playerReady not allowed in {0}", state); //NON-NLS - else + else if (validMap(msg)) playerReady(getPlayerById(from), msg.getShips()); + else { + LOGGER.log(Level.ERROR, "map does not fit game details"); //NON-NLS + send(getPlayerById(from), new GameDetails(config)); + } + } + + /** + * Validates the received map message by checking if all ships are within bounds + * and do not overlap with each other. + * + * @param msg the received MapMessage containing the ships + * @return true if the map is valid, false otherwise + */ + private boolean validMap(MapMessage msg) { + List ships = msg.getShips(); + return inBounds(ships) && !overlaps(ships); + } + + /** + * Checks if all ships in the given list are within the bounds of the map. + * + * @param ships the list of Battleships to validate + * @return true if all ships are within bounds, false otherwise + */ + private boolean inBounds(List ships) { + for (Battleship ship : ships) { + if (!isWithinBounds(ship)) { + return false; + } + } + return true; + } + + /** + * Checks if the given ship is within the bounds of the map. + * + * @param ship the Battleship to check + * @return true if the ship is within bounds, false otherwise + */ + private boolean isWithinBounds(Battleship ship) { + int x1 = ship.getX(); + int y1 = ship.getY(); + Rotation r = ship.getRot(); + int x2 = x1 + ship.getLength() * r.dx(); + int y2 = y1 + ship.getLength() * r.dy(); + + return x1 >= 0 && y1 >= 0 && x1 <= config.getMapWidth() && y1 <= config.getMapHeight() && + x2 >= 0 && y2 >= 0 && x2 <= config.getMapWidth() && y2 <= config.getMapHeight(); + } + + /** + * Checks if any ships in the given list overlap with each other. + * + * @param ships the list of Battleships to validate + * @return true if any ships overlap, false otherwise + */ + private boolean overlaps(List ships) { + for (int i = 0; i < ships.size(); i++) { + Battleship ship1 = ships.get(i); + for (int j = i + 1; j < ships.size(); j++) { + Battleship ship2 = ships.get(j); + if (ship1.collidesWith(ship2)) { + return true; // Collision detected + } + } + } + return false; } /** diff --git a/Projekte/battleship/model/src/main/java/pp/battleship/model/dto/ShipMapDTO.java b/Projekte/battleship/model/src/main/java/pp/battleship/model/dto/ShipMapDTO.java index 9d53146c..e94adfa8 100644 --- a/Projekte/battleship/model/src/main/java/pp/battleship/model/dto/ShipMapDTO.java +++ b/Projekte/battleship/model/src/main/java/pp/battleship/model/dto/ShipMapDTO.java @@ -114,4 +114,22 @@ public static ShipMapDTO loadFrom(File file) throws IOException { throw new IOException(e.getLocalizedMessage()); } } + + /** + * Returns the width of the ship map. + * + * @return the width of the ship map + */ + public int getWidth() { + return width; + } + + /** + * Returns the height of the ship map. + * + * @return the height of the ship map + */ + public int getHeight() { + return height; + } } diff --git a/Projekte/battleship/model/src/main/resources/battleship.properties b/Projekte/battleship/model/src/main/resources/battleship.properties index 47f1f6cf..537f0d3a 100644 --- a/Projekte/battleship/model/src/main/resources/battleship.properties +++ b/Projekte/battleship/model/src/main/resources/battleship.properties @@ -37,3 +37,4 @@ dialog.error=Error dialog.question=Question port.must.be.integer=Port must be an integer number map.doesnt.fit=The map doesn't fit to this game +map.invalid=The map is invalid diff --git a/Projekte/battleship/model/src/main/resources/battleship_de.properties b/Projekte/battleship/model/src/main/resources/battleship_de.properties index 40394202..6aaeea4a 100644 --- a/Projekte/battleship/model/src/main/resources/battleship_de.properties +++ b/Projekte/battleship/model/src/main/resources/battleship_de.properties @@ -27,7 +27,7 @@ host.name=Host port.number=Port wait.its.not.your.turn=Warte, Du bist nicht dran!! menu.quit=Spiel beenden -menu.return-to-game=Zurück zum Spiel +menu.return-to-game=Zurück zum Spiel menu.sound-enabled=Sound eingeschaltet menu.map.load=Karte von Datei laden... menu.map.save=Karte in Datei speichern... @@ -37,3 +37,4 @@ dialog.error=Fehler dialog.question=Frage port.must.be.integer=Der Port muss eine ganze Zahl sein map.doesnt.fit=Diese Karte passt nicht zu diesem Spiel +map.invalid=Die Karte ist ungültig