diff --git a/src/main/java/inf112/fiasko/roborally/elementproperties/GameState.java b/src/main/java/inf112/fiasko/roborally/elementproperties/GameState.java index 4280dd0..bcc5102 100644 --- a/src/main/java/inf112/fiasko/roborally/elementproperties/GameState.java +++ b/src/main/java/inf112/fiasko/roborally/elementproperties/GameState.java @@ -37,10 +37,6 @@ public enum GameState { * Indicates that the game is won by a player */ GAME_IS_WON, - /** - * Indicates that the game is currently waiting for something - */ - LOADING, /** * Indicates that the game is no longer running */ diff --git a/src/main/java/inf112/fiasko/roborally/gamewrapper/RoboRallyWrapper.java b/src/main/java/inf112/fiasko/roborally/gamewrapper/RoboRallyWrapper.java index c1cde41..3212404 100644 --- a/src/main/java/inf112/fiasko/roborally/gamewrapper/RoboRallyWrapper.java +++ b/src/main/java/inf112/fiasko/roborally/gamewrapper/RoboRallyWrapper.java @@ -18,6 +18,8 @@ public class RoboRallyWrapper extends Game { public RoboRallyGame roboRallyGame; public RoboRallyServer server; public RoboRallyClient client; + public int defaultTCPPort = 54555; + public int discoverUDPPort = 54777; @Override public void create() { diff --git a/src/main/java/inf112/fiasko/roborally/gamewrapper/screens/IPAddressScreen.java b/src/main/java/inf112/fiasko/roborally/gamewrapper/screens/IPAddressScreen.java index ea5899b..f3382e3 100644 --- a/src/main/java/inf112/fiasko/roborally/gamewrapper/screens/IPAddressScreen.java +++ b/src/main/java/inf112/fiasko/roborally/gamewrapper/screens/IPAddressScreen.java @@ -1,22 +1,25 @@ package inf112.fiasko.roborally.gamewrapper.screens; import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.OrthographicCamera; import com.badlogic.gdx.scenes.scene2d.InputEvent; import com.badlogic.gdx.scenes.scene2d.Stage; +import com.badlogic.gdx.scenes.scene2d.ui.SelectBox; import com.badlogic.gdx.scenes.scene2d.ui.Skin; import com.badlogic.gdx.scenes.scene2d.ui.TextButton; import com.badlogic.gdx.scenes.scene2d.ui.TextField; import com.badlogic.gdx.scenes.scene2d.utils.ClickListener; import com.badlogic.gdx.utils.viewport.FitViewport; -import com.badlogic.gdx.utils.viewport.Viewport; import inf112.fiasko.roborally.gamewrapper.RoboRallyWrapper; import inf112.fiasko.roborally.networking.RoboRallyClient; import javax.swing.*; import java.io.IOException; +import java.net.InetAddress; +import java.util.HashSet; +import java.util.List; +import java.util.Set; /** * This screen allows the user to enter the ip address to connect to @@ -38,6 +41,20 @@ public class IPAddressScreen extends AbstractScreen { TextButton joinButton = new TextButton("Join", skin); joinButton.setSize(300, 60); joinButton.setPosition(applicationWidth / 2f - joinButton.getWidth() / 2f, 300); + roboRallyWrapper.client = new RoboRallyClient(roboRallyWrapper); + List lanServers = roboRallyWrapper.client.getLanServers(); + Set validHosts = new HashSet<>(); + for (InetAddress address : lanServers) { + validHosts.add(address.getHostAddress()); + } + final SelectBox selectBox = new SelectBox<>(skin); + selectBox.setItems(validHosts.toArray(new String[0])); + selectBox.setPosition(-80 + (applicationWidth - selectBox.getWidth()) / 2f, 200); + selectBox.setSize(200, 50); + + stage.addActor(selectBox); + + joinButton.addListener(new ClickListener() { @Override public boolean touchDown(InputEvent e, float x, float y, int point, int button) { @@ -47,10 +64,16 @@ public class IPAddressScreen extends AbstractScreen { @Override public void touchUp(InputEvent e, float x, float y, int point, int button) { try { - roboRallyWrapper.client = new RoboRallyClient(textInput.getText(), roboRallyWrapper); + String serverIp; + String writtenIP = textInput.getText(); + if (writtenIP.isEmpty()) { + serverIp = selectBox.getSelected(); + } else { + serverIp = writtenIP; + } + roboRallyWrapper.client.connect(serverIp, roboRallyWrapper.defaultTCPPort, roboRallyWrapper.discoverUDPPort); roboRallyWrapper.setScreen(roboRallyWrapper.screenManager.getUsernameScreen(roboRallyWrapper)); } catch (IOException ex) { - ex.printStackTrace(); JOptionPane.showMessageDialog(null, "Could not connect to the server." + " Please make sure the ip address you typed is correct, and that the server is online."); } @@ -80,7 +103,8 @@ public class IPAddressScreen extends AbstractScreen { roboRallyWrapper.batch.setProjectionMatrix(camera.combined); roboRallyWrapper.batch.begin(); - roboRallyWrapper.font.draw(roboRallyWrapper.batch, "Enter IP address and click the button to join a server", + roboRallyWrapper.font.draw(roboRallyWrapper.batch, "Enter or select IP address and click the button to " + + "join a server", applicationWidth / 2f - 380 / 2f, applicationHeight / 2f + 100, 380, 1, true); roboRallyWrapper.batch.end(); diff --git a/src/main/java/inf112/fiasko/roborally/gamewrapper/screens/LoadingScreen.java b/src/main/java/inf112/fiasko/roborally/gamewrapper/screens/LoadingScreen.java index 296a79e..9164c8d 100644 --- a/src/main/java/inf112/fiasko/roborally/gamewrapper/screens/LoadingScreen.java +++ b/src/main/java/inf112/fiasko/roborally/gamewrapper/screens/LoadingScreen.java @@ -44,9 +44,7 @@ public class LoadingScreen extends AbstractScreen { if (roboRallyWrapper.roboRallyGame != null) { GameState gameState = roboRallyWrapper.roboRallyGame.getGameState(); - if (gameState != GameState.LOADING) { - handleScreenChange(gameState); - } + handleScreenChange(gameState); } } @@ -73,12 +71,12 @@ public class LoadingScreen extends AbstractScreen { roboRallyWrapper.setScreen(roboRallyWrapper.screenManager.getPowerDownScreen(this.roboRallyWrapper)); break; case SKIP_POWER_DOWN_SCREEN: - roboRallyWrapper.roboRallyGame.setGameState(GameState.LOADING); + roboRallyWrapper.roboRallyGame.setGameState(GameState.WAITING_FOR_OTHER_PLAYERS_PROGRAMS); roboRallyWrapper.setScreen(roboRallyWrapper.screenManager.getLoadingScreen(this.roboRallyWrapper)); roboRallyWrapper.client.sendElement(new ProgramAndPowerdownRequest(false, new ArrayList<>())); break; default: - System.out.println("The loading screen doesn't know what to do with " + gameState); + //Ignored break; } } diff --git a/src/main/java/inf112/fiasko/roborally/gamewrapper/screens/PowerDownScreen.java b/src/main/java/inf112/fiasko/roborally/gamewrapper/screens/PowerDownScreen.java index 4f121b9..b917871 100644 --- a/src/main/java/inf112/fiasko/roborally/gamewrapper/screens/PowerDownScreen.java +++ b/src/main/java/inf112/fiasko/roborally/gamewrapper/screens/PowerDownScreen.java @@ -56,9 +56,9 @@ public class PowerDownScreen extends AbstractScreen { roboRallyWrapper.batch.setProjectionMatrix(camera.combined); String descriptionText; if (roboRallyWrapper.roboRallyGame.getGameState() == GameState.CHOOSING_POWER_DOWN) { - descriptionText = "Click the button to enter power down next round"; + descriptionText = "Click the button to enter power down next turn"; } else { - descriptionText = "Click the button to continue your power down the next round"; + descriptionText = "Click the button to continue your power down the next turn"; } int elapsedTime = (int) Math.floor((System.currentTimeMillis() - startTime) / 1000f); @@ -90,7 +90,7 @@ public class PowerDownScreen extends AbstractScreen { roboRallyWrapper.client.sendElement(bool); break; case CHOOSING_POWER_DOWN: - roboRallyWrapper.roboRallyGame.setGameState(GameState.LOADING); + roboRallyWrapper.roboRallyGame.setGameState(GameState.WAITING_FOR_OTHER_PLAYERS_PROGRAMS); roboRallyWrapper.setScreen(roboRallyWrapper.screenManager.getLoadingScreen(this.roboRallyWrapper)); roboRallyWrapper.client.sendElement(new ProgramAndPowerdownRequest(bool, roboRallyWrapper.roboRallyGame.getProgram())); diff --git a/src/main/java/inf112/fiasko/roborally/gamewrapper/screens/StartMenuScreen.java b/src/main/java/inf112/fiasko/roborally/gamewrapper/screens/StartMenuScreen.java index 57b08db..621e883 100644 --- a/src/main/java/inf112/fiasko/roborally/gamewrapper/screens/StartMenuScreen.java +++ b/src/main/java/inf112/fiasko/roborally/gamewrapper/screens/StartMenuScreen.java @@ -39,11 +39,11 @@ public class StartMenuScreen extends AbstractScreen { @Override public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) { try { - roboRallyWrapper.server = new RoboRallyServer(); - roboRallyWrapper.client = new RoboRallyClient("127.0.0.1", roboRallyWrapper); + roboRallyWrapper.server = new RoboRallyServer(roboRallyWrapper.defaultTCPPort, roboRallyWrapper.discoverUDPPort); + roboRallyWrapper.client = new RoboRallyClient(roboRallyWrapper); + roboRallyWrapper.client.connect("127.0.0.1", roboRallyWrapper.defaultTCPPort, roboRallyWrapper.discoverUDPPort); roboRallyWrapper.setScreen(roboRallyWrapper.screenManager.getUsernameScreen(roboRallyWrapper)); } catch (IOException e) { - e.printStackTrace(); //Hard fail roboRallyWrapper.quit("Server could not be started"); } diff --git a/src/main/java/inf112/fiasko/roborally/gamewrapper/screens/UsernameScreen.java b/src/main/java/inf112/fiasko/roborally/gamewrapper/screens/UsernameScreen.java index d12a1f5..d1980af 100644 --- a/src/main/java/inf112/fiasko/roborally/gamewrapper/screens/UsernameScreen.java +++ b/src/main/java/inf112/fiasko/roborally/gamewrapper/screens/UsernameScreen.java @@ -1,7 +1,6 @@ package inf112.fiasko.roborally.gamewrapper.screens; import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.OrthographicCamera; import com.badlogic.gdx.scenes.scene2d.InputEvent; import com.badlogic.gdx.scenes.scene2d.Stage; @@ -47,12 +46,12 @@ public class UsernameScreen extends AbstractScreen { JOptionPane.showMessageDialog(null, "Username cannot be empty."); return; } - roboRallyWrapper.client.sendElement(userName); if (roboRallyWrapper.server == null) { roboRallyWrapper.setScreen(roboRallyWrapper.screenManager.getLoadingScreen(roboRallyWrapper)); } else { roboRallyWrapper.setScreen(roboRallyWrapper.screenManager.getLobbyScreen(roboRallyWrapper)); } + roboRallyWrapper.client.sendElement(userName); } }); textInput = new TextField("", skin); diff --git a/src/main/java/inf112/fiasko/roborally/networking/RoboRallyClient.java b/src/main/java/inf112/fiasko/roborally/networking/RoboRallyClient.java index 7d212ca..e23564c 100644 --- a/src/main/java/inf112/fiasko/roborally/networking/RoboRallyClient.java +++ b/src/main/java/inf112/fiasko/roborally/networking/RoboRallyClient.java @@ -5,27 +5,48 @@ import inf112.fiasko.roborally.gamewrapper.RoboRallyWrapper; import inf112.fiasko.roborally.utility.NetworkUtil; import java.io.IOException; +import java.net.InetAddress; +import java.util.List; /** * This class represents a client capable of connecting to a Robo Rally server */ public class RoboRallyClient { private final Client client; + private RoboRallyWrapper wrapper; /** * Instantiates a new Robo Rally client * - * @param ipAddress The ip address of the server to connect to - * @param wrapper The Robo Rally wrapper to be used - * @throws IOException If the server cannot be reached + * @param wrapper The Robo Rally wrapper to be used */ - public RoboRallyClient(String ipAddress, RoboRallyWrapper wrapper) throws IOException { + public RoboRallyClient(RoboRallyWrapper wrapper) { + this.wrapper = wrapper; client = new Client(); client.start(); NetworkUtil.registerClasses(client.getKryo()); - client.connect(5000, ipAddress, 54555); client.addListener(new RoboRallyClientListener(wrapper)); + } + /** + * Connects to a Robo Rally server + * + * @param ipAddress The ip address of the server to join + * @param TCPPort The TCP port to connect to + * @param UDPPort The UDP port to connect to + * @throws IOException If the server cannot be connected to + */ + public void connect(String ipAddress, int TCPPort, int UDPPort) throws IOException { + client.connect(5000, ipAddress, TCPPort, UDPPort); + } + + /** + * Gets a list of addresses of local Robo Rally servers + * + * @return A list of server ip addresses + */ + public List getLanServers() { + return client.discoverHosts(wrapper.discoverUDPPort, 1000); } /** diff --git a/src/main/java/inf112/fiasko/roborally/networking/RoboRallyClientListener.java b/src/main/java/inf112/fiasko/roborally/networking/RoboRallyClientListener.java index 53a90c8..0c496e7 100644 --- a/src/main/java/inf112/fiasko/roborally/networking/RoboRallyClientListener.java +++ b/src/main/java/inf112/fiasko/roborally/networking/RoboRallyClientListener.java @@ -46,6 +46,7 @@ class RoboRallyClientListener extends Listener { GameStartInfoResponse info = (GameStartInfoResponse) object; wrapper.roboRallyGame = new RoboRallyGame(info.getPlayerList(), info.getBoardName(), wrapper.server != null, info.getPlayerName(), wrapper.server); + new Thread(() -> wrapper.roboRallyGame.runTurn()).start(); } else if (object instanceof ProgrammingCardDeck) { if (((ProgrammingCardDeck) object).isEmpty()) { wrapper.roboRallyGame.setProgram(new ArrayList<>()); @@ -57,7 +58,7 @@ class RoboRallyClientListener extends Listener { } else { wrapper.roboRallyGame.setGameState(GameState.CHOOSING_CARDS); } - new Thread(() -> wrapper.roboRallyGame.setPlayerHand((ProgrammingCardDeck) object)).start(); + wrapper.roboRallyGame.setPlayerHand((ProgrammingCardDeck) object); } else if (object instanceof ProgramsContainerResponse) { new Thread(() -> { try { diff --git a/src/main/java/inf112/fiasko/roborally/networking/RoboRallyServer.java b/src/main/java/inf112/fiasko/roborally/networking/RoboRallyServer.java index 4cd3a25..2aa1a08 100644 --- a/src/main/java/inf112/fiasko/roborally/networking/RoboRallyServer.java +++ b/src/main/java/inf112/fiasko/roborally/networking/RoboRallyServer.java @@ -6,6 +6,7 @@ import inf112.fiasko.roborally.elementproperties.RobotID; import inf112.fiasko.roborally.utility.NetworkUtil; import java.io.IOException; +import java.net.InetSocketAddress; import java.util.List; import java.util.Map; @@ -19,13 +20,15 @@ public class RoboRallyServer { /** * Instantiates a new Robo Rally server * + * @param TCPPort The TCP port to bind to + * @param UDPPort The UDP port to bind to * @throws IOException If the server cannot be started */ - public RoboRallyServer() throws IOException { + public RoboRallyServer(int TCPPort, int UDPPort) throws IOException { server = new Server(); server.start(); NetworkUtil.registerClasses(server.getKryo()); - server.bind(54555); + server.bind(TCPPort, UDPPort); listener = new RoboRallyServerListener(this); server.addListener(listener); } diff --git a/src/main/java/inf112/fiasko/roborally/networking/RoboRallyServerListener.java b/src/main/java/inf112/fiasko/roborally/networking/RoboRallyServerListener.java index 2e9ef89..84866fe 100644 --- a/src/main/java/inf112/fiasko/roborally/networking/RoboRallyServerListener.java +++ b/src/main/java/inf112/fiasko/roborally/networking/RoboRallyServerListener.java @@ -85,7 +85,13 @@ class RoboRallyServerListener extends Listener { * @return A mapping between connections and robot ids */ public Map getPlayerNames() { - return playerNames; + Map alivePlayers = new HashMap<>(); + for (Connection connection : playerNames.keySet()) { + if (!deadPlayers.contains(connection)) { + alivePlayers.put(connection, playerNames.get(connection)); + } + } + return alivePlayers; } /** diff --git a/src/main/java/inf112/fiasko/roborally/objects/Board.java b/src/main/java/inf112/fiasko/roborally/objects/Board.java index 3bd87a7..4ab45d4 100644 --- a/src/main/java/inf112/fiasko/roborally/objects/Board.java +++ b/src/main/java/inf112/fiasko/roborally/objects/Board.java @@ -643,7 +643,7 @@ public class Board { * Kills the robot * *

If the robot steps outside of the board, steps on a hole or takes too much damage, this method should be used to - * properly dispose of the robot until the next round.

+ * properly dispose of the robot until the next turn.

* * @param robot The robot to kill */ diff --git a/src/main/java/inf112/fiasko/roborally/objects/Player.java b/src/main/java/inf112/fiasko/roborally/objects/Player.java index 5757020..9d8a682 100644 --- a/src/main/java/inf112/fiasko/roborally/objects/Player.java +++ b/src/main/java/inf112/fiasko/roborally/objects/Player.java @@ -114,7 +114,7 @@ public class Player { /** * Sets the power down status * - * @param powerDownStatus Whether the player is to take power down next round + * @param powerDownStatus Whether the player is to take power down next turn */ public void setPowerDownNextRound(boolean powerDownStatus) { this.powerDownNextRound = powerDownStatus; diff --git a/src/main/java/inf112/fiasko/roborally/objects/RoboRallyGame.java b/src/main/java/inf112/fiasko/roborally/objects/RoboRallyGame.java index 2d6b00b..4cd9aa9 100644 --- a/src/main/java/inf112/fiasko/roborally/objects/RoboRallyGame.java +++ b/src/main/java/inf112/fiasko/roborally/objects/RoboRallyGame.java @@ -24,7 +24,7 @@ public class RoboRallyGame implements DrawableGame, InteractableGame { private final boolean host; private final String playerName; private final RoboRallyServer server; - private final Phase phase; + private Phase phase; private Board gameBoard; private List> repairTiles; private Deck mainDeck; @@ -49,7 +49,6 @@ public class RoboRallyGame implements DrawableGame, InteractableGame { this.playerList = playerList; this.server = server; initializeGame(boardName); - this.phase = new Phase(gameBoard, playerList, 600, this); } /** @@ -187,8 +186,11 @@ public class RoboRallyGame implements DrawableGame, InteractableGame { player.setPowerDownNextRound(powerDowns.getPowerDown().get(player.getName())); } } + //Respawns robots and registers robots which are dead forever respawnRobots(); + //Sends list of dead players to server and removes dead players from the player list sendAllDeadPlayersToServer(); + //Resets hasTouchedFlagThisTurn resetHasTouchedFlagThisTurnForAllRobots(); setGameState(GameState.BEGINNING_OF_GAME); runTurn(); @@ -227,7 +229,7 @@ public class RoboRallyGame implements DrawableGame, InteractableGame { mainDeck = DeckLoaderUtil.loadProgrammingCardsDeck(); } - new Thread(this::runTurn).start(); + phase = new Phase(gameBoard, playerList, 600, this); } catch (IOException e) { e.printStackTrace(); } @@ -249,19 +251,21 @@ public class RoboRallyGame implements DrawableGame, InteractableGame { gameBoard.setBackupPositionOfRobot(robotID, spawnTileContainer.getPosition()); } - } /** - * Runs all the steps of one turn in the game + * Starts a turn in the game */ - private void runTurn() { + public void runTurn() { // Sets the power down status to true on robots that have players who planned one this turn. // Resets players power down for next turn to false. updateRobotPowerDown(); // Set damage of robots in power down to 0 gameBoard.executePowerDown(); - setGameState(GameState.LOADING); + //This check prevents the state from being overwritten if the client has already received the cards + if (gameState == GameState.BEGINNING_OF_GAME) { + setGameState(GameState.WAITING_FOR_CARDS_FROM_SERVER); + } if (host) { //Distributes programming cards for all players, and sends a deck to each player distributeProgrammingCardsToPlayers(); @@ -270,6 +274,8 @@ public class RoboRallyGame implements DrawableGame, InteractableGame { Player player = getPlayerFromName(playerName); if (player != null && player.getProgrammingCardDeck() != null) { server.sendToClient(connection, player.getProgrammingCardDeck()); + } else { + throw new IllegalArgumentException("Player " + playerName + " is not part of the game."); } } } @@ -279,9 +285,6 @@ public class RoboRallyGame implements DrawableGame, InteractableGame { * Sends information about players no longer part of the game to the server */ private void sendAllDeadPlayersToServer() { - if (host) { - server.setDeadPlayers(gameBoard.getRealDeadRobots()); - } //Removes dead players from playerList playerList.removeIf((player) -> gameBoard.getRealDeadRobots().contains(player.getRobotID())); if (playerList.isEmpty()) { @@ -292,7 +295,9 @@ public class RoboRallyGame implements DrawableGame, InteractableGame { e.printStackTrace(); } } - + if (host) { + server.setDeadPlayers(gameBoard.getRealDeadRobots()); + } } /** @@ -406,7 +411,7 @@ public class RoboRallyGame implements DrawableGame, InteractableGame { } /** - * Sets the robot's power down status to the player's "power down next round" status and sets the players status to false + * Sets the robot's power down status to the player's "power down next turn" status and sets the players status to false */ private void updateRobotPowerDown() { for (Player player : playerList) {