diff --git a/src/main/java/seng302/client/ClientPacketParser.java b/src/main/java/seng302/client/ClientPacketParser.java index 4198dc55..e8604936 100644 --- a/src/main/java/seng302/client/ClientPacketParser.java +++ b/src/main/java/seng302/client/ClientPacketParser.java @@ -59,6 +59,7 @@ public class ClientPacketParser { */ public ClientPacketParser() { } + /** * Looks at the type of the packet then sends it to the appropriate parser to extract the * specific data associated with that packet type @@ -108,7 +109,7 @@ public class ClientPacketParser { } } catch (NullPointerException e) { System.out.println("Error parsing packet"); - e.printStackTrace(); +// e.printStackTrace(); } } @@ -185,7 +186,6 @@ public class ClientPacketParser { int noBoats = payload[22]; int raceType = payload[23]; - clientStateBoats = ClientState.getBoats(); for (int i = 0; i < noBoats; i++) { long boatStatusSourceID = bytesToLong( Arrays.copyOfRange(payload, 24 + (i * 20), 28 + (i * 20))); @@ -206,7 +206,9 @@ public class ClientPacketParser { boat.setEstimateTimeAtNextMark(estTimeAtNextMark); boat.setEstimateTimeAtFinish(estTimeAtFinish); - Yacht clientBoat = clientStateBoats.get((int) boatStatusSourceID); + // Update Client State boats when receive race status packet. + // Potentially could replace boats in ClientPacketParser. + Yacht clientBoat = ClientState.getBoats().get((int) boatStatusSourceID); clientBoat.setBoatStatus((boatStatus)); setBoatLegPosition(clientBoat, boatLegNumber); clientBoat.setPenaltiesAwarded(boatPenaltyAwarded); @@ -215,26 +217,32 @@ public class ClientPacketParser { clientBoat.setEstimateTimeAtFinish(estTimeAtFinish); } - // 3 is race started + // 3 is race started. + // ClientState race started flag will be set to true if race started, else set false. if (raceStatus == 3) { ClientState.setRaceStarted(true); + } else { + ClientState.setRaceStarted(false); } } private static void setBoatLegPosition(Yacht updatingBoat, Integer leg){ Integer placing = 1; - if (leg != updatingBoat.getLegNumber() && (raceStarted || raceFinished)) { + + if (/* TODO implement when we are getting this data /TODO leg != updatingBoat.getLegNumber() && */(raceStarted || raceFinished)) { for (Yacht boat : boats.values()) { + placing = boat.getSourceId(); + /* See above to-do if (boat.getLegNumber() != null && leg <= boat.getLegNumber()){ placing += 1; - } + }*/ } updatingBoat.setPosition(placing.toString()); updatingBoat.setLegNumber(leg); boatsPos.putIfAbsent(placing, updatingBoat); boatsPos.replace(placing, updatingBoat); } else if(updatingBoat.getLegNumber() == null){ - updatingBoat.setPosition("1"); + updatingBoat.setPosition("-"); updatingBoat.setLegNumber(leg); } } @@ -286,8 +294,10 @@ public class ClientPacketParser { xmlObject.constructXML(doc, messageType); if (messageType == 7) { //7 is the boat XML boats = xmlObject.getBoatXML().getCompetingBoats(); + // Set/Update the ClientState boats after receiving new boat xml. + // Flag boatsUpdated in ClientState to true. ClientState.setBoats(xmlObject.getBoatXML().getCompetingBoats()); - ClientState.setDirtyState(true); + ClientState.setBoatsUpdated(true); } if (messageType == 6) { //6 is race info xml newRaceXmlReceived = true; diff --git a/src/main/java/seng302/client/ClientState.java b/src/main/java/seng302/client/ClientState.java index 64512a1b..053bc56f 100644 --- a/src/main/java/seng302/client/ClientState.java +++ b/src/main/java/seng302/client/ClientState.java @@ -1,14 +1,12 @@ package seng302.client; -import com.sun.org.apache.xpath.internal.operations.Bool; -import java.util.ArrayList; -import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import seng302.models.Yacht; /** - * Used by the client to store static variables to be used in game. + * Used by the client to store static variables, which other threads and classes + * observer so that they can update their status accordingly. */ public class ClientState { @@ -17,7 +15,7 @@ public class ClientState { private static Boolean raceStarted = false; private static Boolean connectedToHost = false; private static Map boats = new ConcurrentHashMap<>(); - private static Boolean dirtyState = true; + private static Boolean boatsUpdated = true; private static String clientSourceId = ""; public static String getHostIp() { @@ -56,12 +54,12 @@ public class ClientState { return boats; } - public static Boolean isDirtyState() { - return dirtyState; + public static Boolean isBoatsUpdated() { + return boatsUpdated; } - public static void setDirtyState(Boolean dirtyState) { - ClientState.dirtyState = dirtyState; + public static void setBoatsUpdated(Boolean boatsUpdated) { + ClientState.boatsUpdated = boatsUpdated; } public static String getClientSourceId() { diff --git a/src/main/java/seng302/client/ClientStateQueryingRunnable.java b/src/main/java/seng302/client/ClientStateQueryingRunnable.java index 67cf1dbf..23adccb0 100644 --- a/src/main/java/seng302/client/ClientStateQueryingRunnable.java +++ b/src/main/java/seng302/client/ClientStateQueryingRunnable.java @@ -12,6 +12,12 @@ public class ClientStateQueryingRunnable extends Observable implements Runnable public ClientStateQueryingRunnable() {} + /** + * Notifies observers(the lobby controller) that "game started" if ClientState + * raceStarted flag is true and terminates itself. Also, it notifies observers + * to add/remove players if ClientState boatsUpdated flag is true, then resets + * the flag to false; + */ @Override public void run() { while(!terminate) { @@ -29,14 +35,19 @@ public class ClientStateQueryingRunnable extends Observable implements Runnable terminate(); } - if (ClientState.isDirtyState()) { + if (ClientState.isBoatsUpdated()) { setChanged(); notifyObservers("update players"); - ClientState.setDirtyState(false); + ClientState.setBoatsUpdated(false); } } } + /** + * Used to terminate the thread. + * + * Currently called by the main while loop when game started is detected. + */ public void terminate() { terminate = true; } diff --git a/src/main/java/seng302/client/ClientToServerThread.java b/src/main/java/seng302/client/ClientToServerThread.java index 142539d2..da82dff0 100644 --- a/src/main/java/seng302/client/ClientToServerThread.java +++ b/src/main/java/seng302/client/ClientToServerThread.java @@ -15,7 +15,8 @@ import seng302.server.messages.BoatActionMessage; import seng302.server.messages.Message; /** - * Created by kre39 on 13/07/17. + * A class describing a single connection to a Server for the purposes of sending and receiving on + * its own thread. */ public class ClientToServerThread implements Runnable { @@ -30,8 +31,19 @@ public class ClientToServerThread implements Runnable { private OutputStream os; private Boolean updateClient = true; - private ByteArrayOutputStream crcBuffer; + private ByteArrayOutputStream crcBuffer; + /** + * Constructor for ClientToServerThread which takes in ipAddress and portNumber and attempts to + * connect to the specified ipAddress and port. + * + * Upon successful socket connection, threeWayHandshake will be preformed and the instance will + * be put on a thread and run immediately. + * + * @param ipAddress a string of ip address to be connected to + * @param portNumber an integer port number + * @throws Exception SocketConnection if fail to connect to ip address and port number combination + */ public ClientToServerThread(String ipAddress, Integer portNumber) throws Exception{ socket = new Socket(ipAddress, portNumber); is = socket.getInputStream(); @@ -40,7 +52,7 @@ public class ClientToServerThread implements Runnable { Integer allocatedID = threeWayHandshake(); if (allocatedID != null) { ourID = allocatedID; - clientLog("Successful handshake. Allocated ID: " + ourID, 1); + clientLog("Successful handshake. Allocated ID: " + ourID, 0); ClientState.setClientSourceId(String.valueOf(ourID)); } else { clientLog("Unsuccessful handshake", 1); @@ -50,31 +62,31 @@ public class ClientToServerThread implements Runnable { thread = new Thread(this); thread.start(); - } + /** + * Prints out log messages and the time happened. + * Only perform task if log level is below LOG_LEVEL variable. + * + * @param message a string of message to be printed out + * @param logLevel an int for log level + */ static void clientLog(String message, int logLevel){ if(logLevel <= LOG_LEVEL){ System.out.println("[CLIENT " + LocalDateTime.now().toLocalTime().toString() + "] " + message); } } + /** + * Perform the thread loop. It exits the loop if ClientState connected to host + * variable is false. + */ public void run() { int sync1; int sync2; // TODO: 14/07/17 wmu16 - Work out how to fix this while loop while(ClientState.isConnectedToHost()) { try { - //Perform a write if it is time to as delegated by the MainServerThread - if (updateClient) { - // TODO: 13/07/17 wmu16 - Write out game state - some function that would write all appropriate messages to this output stream -// try { -// GameState.outputState(os); -// } catch (IOException e) { -// System.out.println("IO error in server thread upon writing to output stream"); -// } - updateClient = false; - } crcBuffer = new ByteArrayOutputStream(); sync1 = readByte(); sync2 = readByte(); @@ -93,7 +105,6 @@ public class ClientToServerThread implements Runnable { if (computedCrc == packetCrc) { ClientPacketParser .parsePacket(new StreamPacket(type, payloadLength, timeStamp, payload)); - // 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 { clientLog("Packet has been dropped", 1); @@ -101,8 +112,7 @@ public class ClientToServerThread implements Runnable { } } catch (Exception e) { closeSocket(); - System.err.println("SERVER DISCONNECTED"); -// e.printStackTrace(); + clientLog("Disconnected from server", 1); return; } } @@ -112,7 +122,7 @@ public class ClientToServerThread implements Runnable { /** - * Listens for an allocated sourceID and returns it to the server if recieved + * Listens for an allocated sourceID and returns it to the server * @return the sourceID allocated to us by the server */ private Integer threeWayHandshake() { @@ -121,14 +131,15 @@ public class ClientToServerThread implements Runnable { try { ourSourceID = is.read(); } catch (IOException e) { - e.printStackTrace(); + clientLog("Three way handshake failed", 1); + } if (ourSourceID != null) { try { os.write(ourSourceID); return ourSourceID; } catch (IOException e) { - e.printStackTrace(); + clientLog("Three way handshake failed", 1); return null; } } @@ -144,8 +155,7 @@ public class ClientToServerThread implements Runnable { try { os.write(boatActionMessage.getBuffer()); } catch (IOException e) { - clientLog("COULD NOT WRITE TO SERVER", 0); - e.printStackTrace(); + clientLog("Could not write to server", 1); } } @@ -154,7 +164,7 @@ public class ClientToServerThread implements Runnable { try { socket.close(); } catch (IOException e) { - clientLog("Failed to close the socket", 0); + clientLog("Failed to close the socket", 1); } } @@ -165,7 +175,7 @@ public class ClientToServerThread implements Runnable { currentByte = is.read(); crcBuffer.write(currentByte); } catch (IOException e) { - e.printStackTrace(); + clientLog("Read byte failed", 1); } if (currentByte == -1){ throw new Exception(); diff --git a/src/main/java/seng302/controllers/CanvasController.java b/src/main/java/seng302/controllers/CanvasController.java index 03d42872..21902435 100644 --- a/src/main/java/seng302/controllers/CanvasController.java +++ b/src/main/java/seng302/controllers/CanvasController.java @@ -75,7 +75,7 @@ public class CanvasController { private List markGroups = new ArrayList<>(); private List boatGroups = new ArrayList<>(); - private Text FPSdisplay = new Text(); + private Text FPSDisplay = new Text(); private Polygon raceBorder = new Polygon(); //FRAME RATE @@ -120,10 +120,10 @@ public class CanvasController { gc.setGlobalAlpha(0.5); fitMarksToCanvas(); drawGoogleMap(); - FPSdisplay.setLayoutX(5); - FPSdisplay.setLayoutY(20); - FPSdisplay.setStrokeWidth(2); - group.getChildren().add(FPSdisplay); + FPSDisplay.setLayoutX(5); + FPSDisplay.setLayoutY(20); + FPSDisplay.setStrokeWidth(2); + group.getChildren().add(FPSDisplay); group.getChildren().add(raceBorder); initializeMarks(); initializeBoats(); @@ -407,10 +407,10 @@ public class CanvasController { private void drawFps(int fps){ if (raceViewController.isDisplayFps()){ - FPSdisplay.setVisible(true); - FPSdisplay.setText(String.format("%d FPS", fps)); + FPSDisplay.setVisible(true); + FPSDisplay.setText(String.format("%d FPS", fps)); } else { - FPSdisplay.setVisible(false); + FPSDisplay.setVisible(false); } } diff --git a/src/main/java/seng302/controllers/LobbyController.java b/src/main/java/seng302/controllers/LobbyController.java index d65067a7..b5f4f791 100644 --- a/src/main/java/seng302/controllers/LobbyController.java +++ b/src/main/java/seng302/controllers/LobbyController.java @@ -112,6 +112,7 @@ public class LobbyController implements Initializable, Observer{ readyButton.setDisable(true); } + // put all javafx objects in lists, so we can iterate though conveniently imageViews = new ArrayList<>(); Collections.addAll(imageViews, firstImageView, secondImageView, thirdImageView, fourthImageView, fifthImageView, sixthImageView, seventhImageView, eighthImageView); @@ -134,6 +135,13 @@ public class LobbyController implements Initializable, Observer{ clientStateQueryingThread.start(); } + /** + * Observers "ClientStateQueryingRunnable". + * When the clients state has been marked to "race start", the querying thread + * will notify this lobby to change the view + * @param o + * @param arg + */ @Override public void update(Observable o, Object arg) { Platform.runLater(new Runnable() { @@ -149,6 +157,9 @@ public class LobbyController implements Initializable, Observer{ }); } + /** + * Reset all ListViews and ImageViews according to the current competitors + */ private void initialiseListView() { listViews.forEach(listView -> listView.getItems().clear()); imageViews.forEach(gif -> gif.setVisible(false)); @@ -162,6 +173,9 @@ public class LobbyController implements Initializable, Observer{ } } + /** + * Loads preset images into imageViews + */ private void initialiseImageView() { for (int i = 0; i < MAX_NUM_PLAYERS; i++) { imageViews.get(i).setImage(new Image(getClass().getResourceAsStream("/pics/sail.png"))); @@ -195,14 +209,11 @@ public class LobbyController implements Initializable, Observer{ @FXML public void readyButtonPressed() { -// setContentPane("/views/RaceView.fxml"); GameState.setCurrentStage(GameStages.RACING); mainServerThread.startGame(); } - - private void switchToRaceView() { if (!switchedPane) { switchedPane = true; diff --git a/src/main/java/seng302/controllers/RaceViewController.java b/src/main/java/seng302/controllers/RaceViewController.java index 436da210..9bbc0f06 100644 --- a/src/main/java/seng302/controllers/RaceViewController.java +++ b/src/main/java/seng302/controllers/RaceViewController.java @@ -287,6 +287,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel updateWindDirection(); // updateOrder(); updateBoatSelectionComboBox(); + updateOrder(); }) ); @@ -383,9 +384,12 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel } if (ClientPacketParser.isRaceStarted()) { + /* for (Yacht boat : ClientPacketParser.getBoatsPos().values()) { - if (participantIDs.contains(boat.getSourceId())) { // check if the boat is racing - if (boat.getBoatStatus() == 3) { // 3 is finish status + System.out.println("Hi tjere" + boat.getBoatName()); + if (participantIDs.contains(boat.getSourceId()) || true + ) { // check if the boat is racing + if (boat.getBoatStatus() == 69) { // 3 is finish status Text textToAdd = new Text(boat.getPosition() + ". " + boat.getShortName() + " (Finished)"); textToAdd.setFill(Paint.valueOf("#d3d3d3")); @@ -397,9 +401,17 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel textToAdd.setFill(Paint.valueOf("#d3d3d3")); textToAdd.setStyle(""); positionVbox.getChildren().add(textToAdd); + System.out.println("Adding " + textToAdd.getText()); } } } + */ + for (Yacht boat : ClientPacketParser.getBoats().values()){ + Text textToAdd = new Text(boat.getSourceId() + ". " + boat.getShortName() + " "); + textToAdd.setFill(Paint.valueOf("#d3d3d3")); + textToAdd.setStyle(""); + positionVbox.getChildren().add(textToAdd); + } } else { for (Yacht boat : ClientPacketParser.getBoats().values()) { if (participantIDs.contains(boat.getSourceId())) { // check if the boat is racing diff --git a/src/main/java/seng302/gameServer/GameState.java b/src/main/java/seng302/gameServer/GameState.java index e7f4010e..f4ec9c7c 100644 --- a/src/main/java/seng302/gameServer/GameState.java +++ b/src/main/java/seng302/gameServer/GameState.java @@ -26,7 +26,10 @@ public class GameState implements Runnable { private static Map yachts; private static Boolean isRaceStarted; private static GameStages currentStage; - + + private static long startTime = System.currentTimeMillis(); + + public GameState(String hostIpAddress) { windDirection = 180d; windSpeed = 10000d; @@ -80,9 +83,17 @@ public class GameState implements Runnable { } public static void setCurrentStage(GameStages currentStage) { + if (currentStage == GameStages.RACING){ + startTime = System.currentTimeMillis(); + } + GameState.currentStage = currentStage; } + public static long getStartTime(){ + return startTime; + } + public static Double getWindDirection() { return windDirection; } diff --git a/src/main/java/seng302/gameServer/MainServerThread.java b/src/main/java/seng302/gameServer/MainServerThread.java index a5021985..85d5c698 100644 --- a/src/main/java/seng302/gameServer/MainServerThread.java +++ b/src/main/java/seng302/gameServer/MainServerThread.java @@ -11,7 +11,10 @@ import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; +import java.util.Timer; +import java.util.TimerTask; import java.util.concurrent.PriorityBlockingQueue; +import java.util.logging.Logger; /** * A class describing the overall server, which creates and collects server threads for each client @@ -133,8 +136,16 @@ public class MainServerThread extends Observable implements Runnable, ClientConn } public void startGame() { - for (ServerToClientThread serverToClientThread : serverToClientThreads) { - serverToClientThread.sendRaceStatusMessage(); - } + Timer t = new Timer(); + + t.schedule(new TimerTask() { + @Override + public void run() { + + for (ServerToClientThread serverToClientThread : serverToClientThreads) { + serverToClientThread.sendRaceStatusMessage(); + } + } + }, 0, 500); } } diff --git a/src/main/java/seng302/gameServer/ServerToClientThread.java b/src/main/java/seng302/gameServer/ServerToClientThread.java index 6bb2b596..7411310d 100644 --- a/src/main/java/seng302/gameServer/ServerToClientThread.java +++ b/src/main/java/seng302/gameServer/ServerToClientThread.java @@ -340,8 +340,7 @@ public class ServerToClientThread implements Runnable, Observer { 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; @@ -369,7 +368,7 @@ public class ServerToClientThread implements Runnable, Observer { raceStatus = RaceStatus.WARNING; } - sendMessage(new RaceStatusMessage(1, raceStatus, startTime, GameState.getWindDirection(), + sendMessage(new RaceStatusMessage(1, raceStatus, GameState.getStartTime(), GameState.getWindDirection(), GameState.getWindSpeedMMS().longValue(), GameState.getPlayers().size(), RaceType.MATCH_RACE, 1, boatSubMessages)); } diff --git a/src/main/resources/views/RaceView.fxml b/src/main/resources/views/RaceView.fxml index ea453f32..cbe3b2dd 100644 --- a/src/main/resources/views/RaceView.fxml +++ b/src/main/resources/views/RaceView.fxml @@ -20,12 +20,12 @@ -