From d1d659b698e99c04e7a5f14725069211556d3ac5 Mon Sep 17 00:00:00 2001 From: Zhi You Tan Date: Tue, 25 Jul 2017 00:01:59 +1200 Subject: [PATCH] Lobby view will switch to race view when received race status packet with race start type. Ready button can only be pressed by host. Once pressed, it will send out race status packet with race start to all clients. #story[1055] --- .../seng302/client/ClientPacketParser.java | 5 + .../client/ClientStateQueryingRunnable.java | 13 +- .../seng302/client/ClientToServerThread.java | 4 +- .../seng302/controllers/LobbyController.java | 40 +- .../controllers/StartScreenController.java | 5 +- .../seng302/gameServer/GameServerThread.java | 372 ------------------ .../seng302/gameServer/MainServerThread.java | 8 +- .../gameServer/ServerToClientThread.java | 49 ++- src/main/resources/views/LobbyView.fxml | 2 +- 9 files changed, 97 insertions(+), 401 deletions(-) delete mode 100644 src/main/java/seng302/gameServer/GameServerThread.java diff --git a/src/main/java/seng302/client/ClientPacketParser.java b/src/main/java/seng302/client/ClientPacketParser.java index e6b51190..06b22818 100644 --- a/src/main/java/seng302/client/ClientPacketParser.java +++ b/src/main/java/seng302/client/ClientPacketParser.java @@ -215,6 +215,11 @@ public class ClientPacketParser { clientBoat.setEstimateTimeAtNextMark(estTimeAtNextMark); clientBoat.setEstimateTimeAtFinish(estTimeAtFinish); } + + // 3 is race started + if (raceStatus == 3) { + ClientState.setRaceStarted(true); + } } private static void setBoatLegPosition(Yacht updatingBoat, Integer leg){ diff --git a/src/main/java/seng302/client/ClientStateQueryingRunnable.java b/src/main/java/seng302/client/ClientStateQueryingRunnable.java index a71d045b..67cf1dbf 100644 --- a/src/main/java/seng302/client/ClientStateQueryingRunnable.java +++ b/src/main/java/seng302/client/ClientStateQueryingRunnable.java @@ -15,10 +15,6 @@ public class ClientStateQueryingRunnable extends Observable implements Runnable @Override public void run() { while(!terminate) { -// if (ClientState.isRaceStarted() && ClientState.isConnectedToHost()) { -// setChanged(); -// notifyObservers(); -// } // Sleeping the thread so it will respond to the if statement below // if you know a better fix, pls tell me :) -ryan try { @@ -26,9 +22,16 @@ public class ClientStateQueryingRunnable extends Observable implements Runnable } catch (InterruptedException e) { e.printStackTrace(); } + + if (ClientState.isRaceStarted() && ClientState.isConnectedToHost()) { + setChanged(); + notifyObservers("game started"); + terminate(); + } + if (ClientState.isDirtyState()) { setChanged(); - notifyObservers(); + notifyObservers("update players"); ClientState.setDirtyState(false); } } diff --git a/src/main/java/seng302/client/ClientToServerThread.java b/src/main/java/seng302/client/ClientToServerThread.java index 0f405819..649ef2de 100644 --- a/src/main/java/seng302/client/ClientToServerThread.java +++ b/src/main/java/seng302/client/ClientToServerThread.java @@ -95,11 +95,9 @@ public class ClientToServerThread implements Runnable { // TODO: 17/07/17 wmu16 - Fix this or maybe we dont need to go through the main server at all!?!? // packetBufferDelegate.addToBuffer(new StreamPacket(type, payloadLength, timeStamp, payload)); } else { - System.err.println("Packet has been dropped"); + clientLog("Packet has been dropped", 1); } - } - } catch (Exception e) { closeSocket(); e.printStackTrace(); diff --git a/src/main/java/seng302/controllers/LobbyController.java b/src/main/java/seng302/controllers/LobbyController.java index 0cc50be9..b139e428 100644 --- a/src/main/java/seng302/controllers/LobbyController.java +++ b/src/main/java/seng302/controllers/LobbyController.java @@ -18,6 +18,7 @@ import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.fxml.Initializable; +import javafx.scene.control.Button; import javafx.scene.control.ListView; import javafx.scene.image.Image; import javafx.scene.image.ImageView; @@ -29,19 +30,20 @@ import seng302.client.ClientState; import seng302.client.ClientStateQueryingRunnable; import seng302.gameServer.GameStages; import seng302.gameServer.GameState; +import seng302.gameServer.MainServerThread; /** * A class describing the actions of the lobby screen * Created by wmu16 on 10/07/17. */ public class LobbyController implements Initializable, Observer{ - - @FXML private GridPane lobbyScreen; @FXML private Text lobbyIpText; @FXML + private Button readyButton; + @FXML private ListView firstListView; @FXML private ListView secondListView; @@ -85,6 +87,9 @@ public class LobbyController implements Initializable, Observer{ private static ObservableList eighthCompetitor = FXCollections.observableArrayList(); private ClientStateQueryingRunnable clientStateQueryingRunnable; + private Boolean switchedPane = false; + private MainServerThread mainServerThread; + private void setContentPane(String jfxUrl) { try { AnchorPane contentPane = (AnchorPane) lobbyScreen.getParent(); @@ -102,10 +107,14 @@ public class LobbyController implements Initializable, Observer{ @Override public void initialize(URL location, ResourceBundle resources) { - if (ClientState.isHost()) + if (ClientState.isHost()) { lobbyIpText.setText("Lobby Host IP: " + ClientState.getHostIp()); - else + readyButton.setDisable(false); + } + else { lobbyIpText.setText("Connected to IP: "); + readyButton.setDisable(true); + } initialiseListView(); // initialiseLobbyControllerThread(); // initialiseImageView(); // parrot gif init @@ -124,9 +133,12 @@ public class LobbyController implements Initializable, Observer{ Platform.runLater(new Runnable() { @Override public void run() { -// switchToRaceView(); - initialiseListView(); -// clientStateQueryingRunnable.terminate(); + if (arg.equals("game started") && !switchedPane) { + switchToRaceView(); + } + if (arg.equals(("update players"))) { + initialiseListView(); + } } }); } @@ -163,8 +175,6 @@ public class LobbyController implements Initializable, Observer{ } } - - firstListView.setItems(firstCompetitor); secondListView.setItems(secondCompetitor); thirdListView.setItems(thirdCompetitor); @@ -220,11 +230,19 @@ public class LobbyController implements Initializable, Observer{ @FXML public void readyButtonPressed() { - setContentPane("/views/RaceView.fxml"); +// setContentPane("/views/RaceView.fxml"); GameState.setCurrentStage(GameStages.RACING); + mainServerThread.startGame(); } private void switchToRaceView() { - setContentPane("/views/RaceView.fxml"); + if (!switchedPane) { + switchedPane = true; + setContentPane("/views/RaceView.fxml"); + } + } + + public void setMainServerThread(MainServerThread mainServerThread) { + this.mainServerThread = mainServerThread; } } diff --git a/src/main/java/seng302/controllers/StartScreenController.java b/src/main/java/seng302/controllers/StartScreenController.java index 5cfceeca..b2503027 100644 --- a/src/main/java/seng302/controllers/StartScreenController.java +++ b/src/main/java/seng302/controllers/StartScreenController.java @@ -72,14 +72,15 @@ public class StartScreenController { String ipAddress = InetAddress.getLocalHost().getHostAddress(); // get the lobby controller so that we can pass the game server thread to it new GameState(getLocalHostIp()); - new MainServerThread(); + MainServerThread mainServerThread = new MainServerThread(); ClientState.setHost(true); // host will connect and handshake to itself after setting up the server // TODO: 24/07/17 wmu16 - Make port number some static global type constant? ClientToServerThread clientToServerThread = new ClientToServerThread(ClientState.getHostIp(), 4942); ClientState.setConnectedToHost(true); controller.setClientToServerThread(clientToServerThread); - setContentPane("/views/LobbyView.fxml"); + LobbyController lobbyController = (LobbyController) setContentPane("/views/LobbyView.fxml"); + lobbyController.setMainServerThread(mainServerThread); } catch (Exception e) { Alert alert = new Alert(AlertType.ERROR); alert.setHeaderText("Cannot host"); diff --git a/src/main/java/seng302/gameServer/GameServerThread.java b/src/main/java/seng302/gameServer/GameServerThread.java deleted file mode 100644 index 3a208f36..00000000 --- a/src/main/java/seng302/gameServer/GameServerThread.java +++ /dev/null @@ -1,372 +0,0 @@ -package seng302.gameServer; - -import seng302.models.Player; -import seng302.models.Yacht; -import seng302.server.messages.*; -import seng302.server.simulator.Boat; -import seng302.server.simulator.Simulator; - -import java.io.IOException; -import java.io.InputStream; -import java.net.InetSocketAddress; -import java.net.SocketOption; -import java.net.SocketOptions; -import java.nio.channels.ServerSocketChannel; -import java.nio.channels.SocketChannel; -import java.util.*; - -public class GameServerThread implements Runnable, Observer, ClientConnectionDelegate{ - - private static final Integer MAX_NUM_PLAYERS = 10; - public static final int PORT_NUMBER = 4942; - - private Boolean hosting = true; - - private ServerSocketChannel server; - private long startTime; - private short seqNum; - - private final int RACE_STATUS_PERIOD = 1000/2; - private final int RACE_START_STATUS_PERIOD = 1000; - private final int BOAT_LOCATION_PERIOD = 1000/5; - private final int TIME_TILL_RACE_START = 20*1000; - private static final int LOG_LEVEL = 1; - - public GameServerThread(String threadName){ - Thread runner = new Thread(this, threadName); - runner.setDaemon(true); - seqNum = 0; - - runner.start(); - } - - public static void serverLog(String message, int logLevel){ - if(logLevel <= LOG_LEVEL){ - System.out.println("[SERVER] " + message); - } - } - - /** - * Creates and returns an XML Message from the file specified - * @param fileName The source XML file - * @param type The XML Message type - * @return The XML Message - */ - private Message getXmlMessage(String fileName, XMLMessageSubType type){ - String fileContents = null; - - try { - InputStream thisStream = this.getClass().getResourceAsStream(fileName); - fileContents = new String(org.apache.commons.io.IOUtils.toByteArray(thisStream)); - } catch (IOException e) { - e.printStackTrace(); - } catch (NullPointerException e){ - return null; - } - - if (fileContents != null){ - return new XMLMessage(fileContents, type, seqNum); - } - - return null; - } - - /** - * @return Get a race status message for the current race - */ - private Message getRaceStatusMessage(){ - - List boatSubMessages = new ArrayList<>(); - BoatStatus boatStatus; - RaceStatus raceStatus; - boolean thereAreBoatsNotFinished = false; - - for (Player player : GameState.getPlayers()){ - Yacht y = player.getYacht(); - - if (GameState.getCurrentStage() == GameStages.PRE_RACE){ - boatStatus = BoatStatus.PRESTART; - thereAreBoatsNotFinished = true; - } - else if(false){ //@TODO if boat has finished - boatStatus = BoatStatus.FINISHED; - } - else{ - boatStatus = BoatStatus.PRESTART; - thereAreBoatsNotFinished = true; - } - - BoatSubMessage m = new BoatSubMessage(y.getSourceId(), boatStatus, y.getLastMarkRounded().getId(), 0, 0, 1234l, 1234l); - boatSubMessages.add(m); - } - - if (thereAreBoatsNotFinished){ - if (GameState.getCurrentStage() == GameStages.RACING){ - raceStatus = RaceStatus.STARTED; - } - else{ - long currentTime = System.currentTimeMillis(); - long timeDifference = startTime - currentTime; - - if (timeDifference > 60*3){ - raceStatus = RaceStatus.PRESTART; - } - else if (timeDifference > 60){ - raceStatus = RaceStatus.WARNING; - } - else{ - raceStatus = RaceStatus.PREPARATORY; - } - } - } - else{ - raceStatus = RaceStatus.TERMINATED; - } - - return new RaceStatusMessage(1, raceStatus, startTime, WindDirection.SOUTH, - 100, GameState.getPlayers().size(), RaceType.MATCH_RACE, 1, boatSubMessages); - } - - /** - * Start sending race start status messages until race starts - */ - private void startSendingRaceStartStatusMessages(){ - Timer t = new Timer(); - t.schedule(new TimerTask() { - @Override - public void run() { - Message raceStartStatusMessage = new RaceStartStatusMessage(seqNum, startTime , 1, - RaceStartNotificationType.SET_RACE_START_TIME); - try { - if (startTime < System.currentTimeMillis() && GameState.getCurrentStage() != GameStages.RACING){ - } - else{ - broadcast(raceStartStatusMessage); - } - - } catch (IOException e) { - e.printStackTrace(); - } - } - }, 0, RACE_START_STATUS_PERIOD); - } - - /** - * Start sending race start status messages until race starts - */ - private void startSendingRaceStatusMessages(){ - - Timer t = new Timer(); - t.schedule(new TimerTask() { - @Override - public void run() { - Message raceStatusMessage = getRaceStatusMessage(); - try { - broadcast(raceStatusMessage); - } catch (IOException e) { - e.printStackTrace(); - } - } - }, 0, RACE_STATUS_PERIOD); - } - - /** - * Sends the race, boat, and regatta XML files to the client - */ - private void sendXml(){ - try{ - Message raceData = getXmlMessage("/server_config/race.xml", XMLMessageSubType.RACE); - Message boatData = getXmlMessage("/server_config/boats.xml", XMLMessageSubType.BOAT); - Message regatta = getXmlMessage("/server_config/regatta.xml", XMLMessageSubType.REGATTA); - - if (raceData != null){ - broadcast(raceData); - } - if (boatData != null){ - broadcast(boatData); - } - if (regatta != null){ - broadcast(regatta); - } - } catch (IOException e) { - serverLog("Couldn't send an XML Message: " + e.getMessage(), 0); - } - } - - /** - * Send the post-start race course information - */ - private void sendPostStartCourseXml(){ - Timer t = new Timer(); - t.schedule(new TimerTask() { - @Override - public void run() { - try { - Message raceData = getXmlMessage("/server_config/courseLimits.xml", XMLMessageSubType.RACE); - if (raceData != null) { - broadcast(raceData); - } - }catch (IOException e) { - serverLog("Couldn't send an XML Message: " + e.getMessage(), 0); - } - } - },1000); - //Delays the new course xml data for 25 seconds so the boats are able to pass the starting line - } - - public void run() { - ServerListenThread serverListenThread; - HeartbeatThread heartbeatThread; - Boolean serverIsSendingMessages = false; - - try{ - server = ServerSocketChannel.open(); - server.socket().bind(new InetSocketAddress("localhost", PORT_NUMBER)); - -// serverListenThread = new ServerListenThread(server, this); - heartbeatThread = new HeartbeatThread(this); - - heartbeatThread.start(); -// serverListenThread.start(); - } - catch (IOException e){ - serverLog("Failed to bind socket: " + e.getMessage(), 0); - } - - while (hosting) { - try { - Thread.sleep(100); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - if (GameState.getCurrentStage() == GameStages.RACING && !serverIsSendingMessages) { - serverLog("Race Started", 0); - - sendXml(); - startSendingRaceStartStatusMessages(); - //startSendingRaceStatusMessages(); - sendPostStartCourseXml(); - serverIsSendingMessages = true; - } - - else if (GameState.getCurrentStage() == GameStages.FINISHED) { - serverLog("Race Finished", 0); - } - - startTime = System.currentTimeMillis() + TIME_TILL_RACE_START; - } - } - -// /** -// * Start sending static boat position updates when race has finished -// */ -// private void startSendingRaceFinishedBoatPositions(){ -// Timer t = new Timer(); -// t.schedule(new TimerTask() { -// @Override -// public void run() { -// try { -// for (Boat b : raceSimulator.getBoats()){ -// Message m = new BoatLocationMessage(b.getSourceID(), seqNum, b.getLat(), -// b.getLng(), b.getLastPassedCorner().getBearingToNextCorner(), -// ((long) 0)); -// -// server.send(m); -// } -// -// } catch (IOException e) { -// e.printStackTrace(); -// } -// } -// }, 0, BOAT_LOCATION_PERIOD); -// } - - /** - * A client has tried to connect to the server - * @param player The player that connected - */ - public void clientConnected(Player player) { - if (GameState.getPlayers().size() < MAX_NUM_PLAYERS && GameState.getCurrentStage() == GameStages.LOBBYING) { - serverLog("Player Connected", 0); - GameState.addPlayer(player); - sendXml(); - } - } - - @Override - public void clientConnected(ServerToClientThread serverToClientThread) { - - } - - /** - * A player has left the game, remove the player from the GameState - * @param player The player that left - */ - @Override - public void clientDisconnected(Player player) { - serverLog("Player disconnected", 0); - GameState.removePlayer(player); - sendXml(); - } - - - void broadcast(Message message) throws IOException{ - for(Player player : GameState.getPlayers()) { - //heh - player.getSocket().getOutputStream().write(message.getBuffer()); - } - seqNum++; - } - - /** - * Send a boat location message when they are updated by the simulator - * @param o . - * @param arg . - */ - @Override - @SuppressWarnings("unchecked") - public void update(Observable o, Object arg) { - /* Only send if server started - // TODO: I don't understand why i need to check server is null or not ... confused - haoming 2/5/17 - if(server == null || !server.isStarted()){ - return; - } - - int numOfBoatsFinished = 0; - for (Boat boat : (List) arg){ - try { - if (boat.isFinished()) { - numOfBoatsFinished ++; - if (!boatsFinished.get(boat.getSourceID())) { - boatsFinished.put(boat.getSourceID(), true); - } - } - Message m = new BoatLocationMessage(boat.getSourceID(), 1, boat.getLat(), - boat.getLng(), boat.getLastPassedCorner().getBearingToNextCorner(), - ((long) boat.getSpeed())); - broadcast(m); - } catch (IOException e) { - serverLog("Couldn't send a boat status message", 3); - return; - } - catch (NullPointerException e){ - e.printStackTrace(); - }*/ - } - -// if (numOfBoatsFinished == ((List) arg).size()) { -// startSendingRaceFinishedBoatPositions(); -// } - - //} - - public void terminateGame() { - try { - //TODO: for now, I just close the socket, but i think we should terminate the whole thread instead. -hyi25 13 July - server.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } -} diff --git a/src/main/java/seng302/gameServer/MainServerThread.java b/src/main/java/seng302/gameServer/MainServerThread.java index 6a74970b..a7c72c77 100644 --- a/src/main/java/seng302/gameServer/MainServerThread.java +++ b/src/main/java/seng302/gameServer/MainServerThread.java @@ -99,9 +99,6 @@ public class MainServerThread extends Observable implements Runnable, PacketBuff } } - - - public void updateClients() { for (ServerToClientThread serverToClientThread : serverToClientThreads) { serverToClientThread.updateClient(); @@ -146,4 +143,9 @@ public class MainServerThread extends Observable implements Runnable, PacketBuff // sendXml(); } + public void startGame() { + for (ServerToClientThread serverToClientThread : serverToClientThreads) { + serverToClientThread.sendRaceStatusMessage(); + } + } } diff --git a/src/main/java/seng302/gameServer/ServerToClientThread.java b/src/main/java/seng302/gameServer/ServerToClientThread.java index 9418e6d9..ee9b93d5 100644 --- a/src/main/java/seng302/gameServer/ServerToClientThread.java +++ b/src/main/java/seng302/gameServer/ServerToClientThread.java @@ -8,6 +8,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.util.ArrayList; +import java.util.List; import java.util.Observable; import java.util.Observer; import java.util.Random; @@ -23,12 +24,18 @@ import seng302.models.xml.Regatta; import seng302.models.xml.XMLGenerator; import seng302.server.messages.BoatActionType; import seng302.server.messages.BoatLocationMessage; +import seng302.server.messages.BoatStatus; +import seng302.server.messages.BoatSubMessage; import seng302.server.messages.Message; import java.io.*; import java.net.Socket; import java.util.zip.CRC32; import java.util.zip.Checksum; +import seng302.server.messages.RaceStatus; +import seng302.server.messages.RaceStatusMessage; +import seng302.server.messages.RaceType; +import seng302.server.messages.WindDirection; import seng302.server.messages.XMLMessage; import seng302.server.messages.XMLMessageSubType; import seng302.server.messages.XMLMessage; @@ -172,6 +179,8 @@ public class ServerToClientThread implements Runnable, Observer { // message = new XMLMessage(xml, XMLMessageSubType.BOAT, 0); // sendMessage(message); // System.out.println("[server] send message 4 " + message); +// sendMessage(getRaceStatusMessage()); +// System.out.println("sent race status"); //------- while(true) { @@ -215,7 +224,7 @@ public class ServerToClientThread implements Runnable, Observer { break; } } else { - System.err.println("Packet has been dropped"); + serverLog("Packet has been dropped", 1); } } } catch (Exception e) { @@ -359,10 +368,42 @@ public class ServerToClientThread implements Runnable, Observer { } } - - - public Thread getThread() { return thread; } + + public void sendRaceStatusMessage(){ + // variables taken from GameServerThread + int TIME_TILL_RACE_START = 20*1000; + long startTime = System.currentTimeMillis() + TIME_TILL_RACE_START; + + List boatSubMessages = new ArrayList<>(); + BoatStatus boatStatus; + RaceStatus raceStatus; + + for (Player player : GameState.getPlayers()){ + Yacht y = player.getYacht(); + + if (GameState.getCurrentStage() == GameStages.PRE_RACE){ + boatStatus = BoatStatus.PRESTART; + } + else if(GameState.getCurrentStage() == GameStages.RACING){ + boatStatus = BoatStatus.RACING; + } else { + boatStatus = BoatStatus.UNDEFINED; + } + + BoatSubMessage m = new BoatSubMessage(y.getSourceId(), boatStatus, 0, 0, 0, 1234l, 1234l); + boatSubMessages.add(m); + } + + if (GameState.getCurrentStage() == GameStages.RACING){ + raceStatus = RaceStatus.STARTED; + } else { + raceStatus = RaceStatus.WARNING; + } + + sendMessage(new RaceStatusMessage(1, raceStatus, startTime, WindDirection.SOUTH, + 100, GameState.getPlayers().size(), RaceType.MATCH_RACE, 1, boatSubMessages)); + } } diff --git a/src/main/resources/views/LobbyView.fxml b/src/main/resources/views/LobbyView.fxml index 1c327227..e867a519 100644 --- a/src/main/resources/views/LobbyView.fxml +++ b/src/main/resources/views/LobbyView.fxml @@ -38,7 +38,7 @@ -