diff --git a/src/main/java/seng302/gameServer/GameState.java b/src/main/java/seng302/gameServer/GameState.java index 0b36ab16..e710d81b 100644 --- a/src/main/java/seng302/gameServer/GameState.java +++ b/src/main/java/seng302/gameServer/GameState.java @@ -15,6 +15,7 @@ import org.w3c.dom.Document; import org.xml.sax.InputSource; import seng302.gameServer.messages.BoatAction; import seng302.gameServer.messages.BoatStatus; +import seng302.gameServer.messages.ChatterMessage; import seng302.gameServer.messages.CustomizeRequestType; import seng302.gameServer.messages.MarkRoundingMessage; import seng302.gameServer.messages.MarkType; @@ -41,7 +42,6 @@ public class GameState implements Runnable { @FunctionalInterface interface NewMessageListener { - void notify(Message message); } @@ -72,19 +72,9 @@ public class GameState implements Runnable { private static Set marks; private static List courseLimit; - private static List markListeners; + private static List messageListeners; private static Map playerStringMap = new HashMap<>(); - /* - Ideally I would like to make this class an object instantiated by the server and given to - it's created threads if necessary. Outside of that I think the dependencies on it - (atm only Yacht & GameClient) can be removed from most other classes. The observable list of - players could be pulled directly from the server by the GameClient since it instantiates it - and it is reasonable for it to pull data. The current setup of publicly available statics is - pretty meh IMO because anything can change it making it unreliable and like people did with - the old ServerParser class everything that needs shared just gets thrown in the static - collections and things become a real mess. - */ public GameState(String hostIpAddress) { windDirection = 180d; @@ -100,7 +90,7 @@ public class GameState implements Runnable { //set this when game stage changes to prerace previousUpdateTime = System.currentTimeMillis(); markOrder = new MarkOrder(); //This could be instantiated at some point with a select map? - markListeners = new ArrayList<>(); + messageListeners = new ArrayList<>(); resetStartTime(); @@ -671,8 +661,8 @@ public class GameState implements Runnable { } private static void notifyMessageListeners(Message message) { - for (NewMessageListener mpl : markListeners) { - mpl.notify(message); + for (NewMessageListener ml : messageListeners) { + ml.notify(message); } } @@ -685,7 +675,7 @@ public class GameState implements Runnable { public static void addMarkPassListener(NewMessageListener listener) { - markListeners.add(listener); + messageListeners.add(listener); } public static void setCustomizationFlag() { @@ -699,4 +689,8 @@ public class GameState implements Runnable { public static void resetCustomizationFlag() { customizationFlag = false; } + + public static void broadcastChatter(ChatterMessage chatterMessage) { + notifyMessageListeners(chatterMessage); + } } diff --git a/src/main/java/seng302/gameServer/ServerPacketParser.java b/src/main/java/seng302/gameServer/ServerPacketParser.java index 62d971ea..f555d62f 100644 --- a/src/main/java/seng302/gameServer/ServerPacketParser.java +++ b/src/main/java/seng302/gameServer/ServerPacketParser.java @@ -2,6 +2,7 @@ package seng302.gameServer; import java.util.Arrays; import seng302.gameServer.messages.BoatAction; +import seng302.gameServer.messages.ChatterMessage; import seng302.gameServer.messages.ClientType; import seng302.gameServer.messages.CustomizeRequestType; import seng302.gameServer.messages.Message; @@ -28,5 +29,11 @@ public class ServerPacketParser { long type = Message.bytesToLong(Arrays.copyOfRange(payload, 4, 5)); return CustomizeRequestType.getRequestType((int) type); } + + public static ChatterMessage extractChatterText(byte[] payload) { + return new ChatterMessage( + payload[1], new String(Arrays.copyOfRange(payload, 3, payload.length)) + ); + } } diff --git a/src/main/java/seng302/gameServer/ServerToClientThread.java b/src/main/java/seng302/gameServer/ServerToClientThread.java index 3a9b4057..1cbba682 100644 --- a/src/main/java/seng302/gameServer/ServerToClientThread.java +++ b/src/main/java/seng302/gameServer/ServerToClientThread.java @@ -22,6 +22,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import seng302.gameServer.messages.BoatAction; import seng302.gameServer.messages.BoatLocationMessage; +import seng302.gameServer.messages.ChatterMessage; import seng302.gameServer.messages.ClientType; import seng302.gameServer.messages.CustomizeRequestType; import seng302.gameServer.messages.Message; @@ -225,7 +226,11 @@ public class ServerToClientThread implements Runnable, Observer { completeRegistration(requestedType); break; - + case CHATTER_TEXT: + GameState.broadcastChatter( + ServerPacketParser.extractChatterText(payload) + ); + break; case RACE_CUSTOMIZATION_REQUEST: Long sourceID = Message .bytesToLong(Arrays.copyOfRange(payload, 0, 3)); diff --git a/src/main/java/seng302/utilities/StreamParser.java b/src/main/java/seng302/utilities/StreamParser.java index 1f90eac8..748337df 100644 --- a/src/main/java/seng302/utilities/StreamParser.java +++ b/src/main/java/seng302/utilities/StreamParser.java @@ -2,9 +2,11 @@ package seng302.utilities; import java.io.IOException; import java.io.StringReader; +import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import javafx.util.Pair; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; @@ -62,31 +64,10 @@ public class StreamParser { long windDir = bytesToLong(Arrays.copyOfRange(payload, 18, 20)); long rawWindSpeed = bytesToLong(Arrays.copyOfRange(payload, 20, 22)); -// DateFormat format = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss"); -// currentTime = format.format((new Date(currentTime))) - RaceStatusData data = new RaceStatusData( windDir, rawWindSpeed, raceStatus, currentTime, expectedStartTime ); -// long timeTillStart = -// ((new Date(expectedStartTime)).getTime() - (new Date(currentTime)).getTime()) / 1000; -// -// if (timeTillStart > 0) { -// timeSinceStart = timeTillStart; -// } else { -// if (raceStatus == 4 || raceStatus == 8) { -// raceFinished = true; -// raceStarted = false; -// } else if (!raceStarted) { -// raceStarted = true; -// raceFinished = false; -// } -// timeSinceStart = timeTillStart; -// } -// - -// int noBoats = payload[22]; int raceType = payload[23]; long boatID, estTimeAtNextMark, estTimeAtFinish; @@ -106,24 +87,6 @@ public class StreamParser { return data; } -// private static void setBoatLegPosition(Yacht updatingBoat, Integer leg){ -// Integer placing = 1; -// if (leg != updatingBoat.getLegNumber() && (raceStarted || raceFinished)) { -// for (Yacht boat : boats.values()) { -// if (boat.getLegNumber() != null && leg <= boat.getLegNumber()){ -// placing += 1; -// } -// } -// updatingBoat.setPlacing(placing.toString()); -// updatingBoat.setLegNumber(leg); -// boatsPos.putIfAbsent(placing, updatingBoat); -// boatsPos.replace(placing, updatingBoat); -// } else if(updatingBoat.getLegNumber() == null){ -// updatingBoat.setPlacing("1"); -// updatingBoat.setLegNumber(leg); -// } -// } - /** * Parses and returns the text from a StreamPacket containing text data for display. * @@ -255,15 +218,15 @@ public class StreamParser { * @return Chatter text message as a string. Returns null if the packet is not of type * CHATTER_TEXT. */ - public static String extractChatterText(StreamPacket packet) { + public static Pair extractChatterText(StreamPacket packet) { if (packet.getType() != PacketType.CHATTER_TEXT) { return null; } byte[] payload = packet.getPayload(); int messageVersionNo = payload[0]; int messageType = payload[1]; - int length = payload[2]; - return new String(Arrays.copyOfRange(payload, 3, 3 + length)); + int length = (int) bytesToLong(new byte[]{payload[2]}); + return new Pair<>(messageType, new String(Arrays.copyOfRange(payload, 3, 3 + length))); } /** @@ -392,26 +355,6 @@ public class StreamParser { }; } - - public static void extractBoatAction(StreamPacket packet) { - byte[] payload = packet.getPayload(); - int messageVersionNo = payload[0]; - long actionType = bytesToLong(Arrays.copyOfRange(payload, 0, 1)); - if (actionType == 1) { - System.out.println("VMG"); - } else if (actionType == 2) { - System.out.println("SAILS IN"); - } else if (actionType == 3) { - System.out.println("SAILS OUT"); - } else if (actionType == 4) { - System.out.println("TACK/GYBE"); - } else if (actionType == 5) { - System.out.println("UPWIND"); - } else if (actionType == 6) { - System.out.println("DOWNWIND"); - } - } - /** * takes an array of up to 7 bytes and returns a positive long constructed from the input bytes * diff --git a/src/main/java/seng302/visualiser/ClientToServerThread.java b/src/main/java/seng302/visualiser/ClientToServerThread.java index d3e9b81f..1ee1dd96 100644 --- a/src/main/java/seng302/visualiser/ClientToServerThread.java +++ b/src/main/java/seng302/visualiser/ClientToServerThread.java @@ -288,7 +288,7 @@ public class ClientToServerThread implements Runnable { } public void sendChatterMessage(String message) { - sendByteBuffer(new ChatterMessage(5, message).getBuffer()); + sendByteBuffer(new ChatterMessage(clientId, message).getBuffer()); } private void sendByteBuffer(byte[] bytes) { diff --git a/src/main/java/seng302/visualiser/GameClient.java b/src/main/java/seng302/visualiser/GameClient.java index f783e476..91fbf5c5 100644 --- a/src/main/java/seng302/visualiser/GameClient.java +++ b/src/main/java/seng302/visualiser/GameClient.java @@ -1,8 +1,12 @@ package seng302.visualiser; import java.io.IOException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; import java.time.ZoneId; import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.util.Date; import java.util.Map; import java.util.TimeZone; import javafx.application.Platform; @@ -15,6 +19,7 @@ import javafx.scene.control.Alert.AlertType; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; import javafx.scene.layout.Pane; +import javafx.util.Pair; import seng302.gameServer.GameState; import seng302.gameServer.MainServerThread; import seng302.gameServer.messages.BoatAction; @@ -198,12 +203,13 @@ public class GameClient { raceView.loadRace(allBoatsMap, courseData, raceState, player); raceView.getSendPressedProperty().addListener((obs, old, isPressed) -> { if (isPressed) { - socketThread.sendChatterMessage(raceView.readChatInput()); + formatAndSendChatMessage(raceView.readChatInput()); } }); } + private void loadFinishScreenView() { FXMLLoader fxmlLoader = loadFXMLToHolder("/views/FinishScreenView.fxml"); FinishScreenViewController controller = fxmlLoader.getController(); @@ -287,6 +293,14 @@ public class GameClient { case YACHT_EVENT_CODE: showCollisionAlert(StreamParser.extractYachtEventCode(packet)); break; + + case CHATTER_TEXT: + Pair playerIdMessagePair = StreamParser + .extractChatterText(packet); + raceView.updateChatHistory( + allBoatsMap.get(playerIdMessagePair.getKey()).getColour(), + playerIdMessagePair.getValue() + ); } } } @@ -382,7 +396,7 @@ public class GameClient { private void keyPressed(KeyEvent e) { if (raceView.isChatInputFocused()) { if (e.getCode() == KeyCode.ENTER) { - socketThread.sendChatterMessage(raceView.readChatInput()); + formatAndSendChatMessage(raceView.readChatInput()); } return; } @@ -433,4 +447,14 @@ public class GameClient { ); } } + + private void formatAndSendChatMessage(String rawChat) { + if (rawChat.length() > 0) { + socketThread.sendChatterMessage( + new SimpleDateFormat("[HH:mm:ss] ").format(new Date()) + + allBoatsMap.get(socketThread.getClientId()).getShortName() + ": " + rawChat + ); + } + } + } diff --git a/src/main/java/seng302/visualiser/controllers/RaceViewController.java b/src/main/java/seng302/visualiser/controllers/RaceViewController.java index eca30bf1..a50f5d89 100644 --- a/src/main/java/seng302/visualiser/controllers/RaceViewController.java +++ b/src/main/java/seng302/visualiser/controllers/RaceViewController.java @@ -57,6 +57,8 @@ import seng302.visualiser.fxObjects.BoatObject; */ public class RaceViewController extends Thread implements ImportantAnnotationDelegate { + private final int CHAT_LIMIT = 128; + @FXML private Pane basePane; @FXML @@ -113,10 +115,14 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel raceSparkLine.visibleProperty().setValue(false); raceSparkLine.getYAxis().setAutoRanging(false); sparklineYAxis.setTickMarkVisible(false); - positionVbox.getStylesheets().add(getClass().getResource("/css/master.css").toString()); selectAnnotationBtn.setOnAction(event -> loadSelectAnnotationView()); + chatInput.lengthProperty().addListener((obs, oldLen, newLen) -> { + if (newLen.intValue() > CHAT_LIMIT) { + chatInput.setText(chatInput.getText().substring(0, CHAT_LIMIT)); + } + }); } public void loadRace ( @@ -650,9 +656,8 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel return chat; } -// @FXML -// public void onSendAction() { -// -// chatHistory.setText(chatHistory.getText() + chatInput.getText() + '\n'); -// } + public void updateChatHistory(Paint playerColour, String newMessage) { + chatHistory.setText(chatHistory.getText() + newMessage + '\n'); + } + } \ No newline at end of file diff --git a/src/main/resources/views/RaceView.fxml b/src/main/resources/views/RaceView.fxml index 5e38aff2..9ec53ff6 100644 --- a/src/main/resources/views/RaceView.fxml +++ b/src/main/resources/views/RaceView.fxml @@ -6,23 +6,6 @@ - - - - - - - - - - - - - - - - - @@ -57,7 +40,8 @@ AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">