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