diff --git a/src/main/java/seng302/gameServer/MainServerThread.java b/src/main/java/seng302/gameServer/MainServerThread.java index 74c5a1dc..cd762501 100644 --- a/src/main/java/seng302/gameServer/MainServerThread.java +++ b/src/main/java/seng302/gameServer/MainServerThread.java @@ -1,10 +1,12 @@ package seng302.gameServer; +import com.sun.corba.se.spi.activation.Server; import java.io.IOException; import java.net.ServerSocket; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Observable; +import java.util.Observer; import java.util.Timer; import java.util.TimerTask; import seng302.model.GeoPoint; @@ -18,7 +20,8 @@ import seng302.visualiser.GameClient; * A class describing the overall server, which creates and collects server threads for each client * Created by wmu16 on 13/07/17. */ -public class MainServerThread extends Observable implements Runnable, ClientConnectionDelegate { +public class MainServerThread extends Observable implements Runnable, ClientConnectionDelegate, + Observer { private static final int PORT = 4942; private static final Integer CLIENT_UPDATES_PER_SECOND = 10; @@ -112,7 +115,7 @@ public class MainServerThread extends Observable implements Runnable, ClientConn serverToClientThreads.add(serverToClientThread); this.addObserver(serverToClientThread); setChanged(); - notifyObservers(); + notifyObservers("send setup message"); } /** @@ -136,11 +139,12 @@ public class MainServerThread extends Observable implements Runnable, ClientConn } } setChanged(); - notifyObservers(); + notifyObservers("send setup message"); } public void startGame() { initialiseBoatPositions(); + setupYachtObserver(); Timer t = new Timer(); @@ -210,4 +214,17 @@ public class MainServerThread extends Observable implements Runnable, ClientConn boatIndex++; } } + + @Override + public void update(Observable o, Object arg) { + for (ServerToClientThread serverToClientThread : serverToClientThreads) { + serverToClientThread.sendCollisionMessage((Integer) arg); + } + } + + private void setupYachtObserver() { + for (ServerToClientThread serverToClientThread : serverToClientThreads) { + serverToClientThread.getYacht().addObserver(this); + } + } } diff --git a/src/main/java/seng302/gameServer/ServerToClientThread.java b/src/main/java/seng302/gameServer/ServerToClientThread.java index c41f7ed0..1cb1d15d 100644 --- a/src/main/java/seng302/gameServer/ServerToClientThread.java +++ b/src/main/java/seng302/gameServer/ServerToClientThread.java @@ -18,6 +18,7 @@ import java.util.concurrent.ThreadLocalRandom; import java.util.stream.Collectors; import java.util.zip.CRC32; import java.util.zip.Checksum; +import seng302.gameServer.server.messages.YachtEventCodeMessage; import seng302.model.Player; import seng302.model.Yacht; import seng302.model.stream.packets.PacketType; @@ -126,7 +127,9 @@ public class ServerToClientThread implements Runnable, Observer { @Override public void update(Observable o, Object arg) { - sendSetupMessages(); + if (arg.equals("send setup message")) { + sendSetupMessages(); + } } public void run() { @@ -372,4 +375,8 @@ public class ServerToClientThread implements Runnable, Observer { public Yacht getYacht() { return yacht; } + + public void sendCollisionMessage(Integer yachtId) { + sendMessage(new YachtEventCodeMessage(yachtId)); + } } diff --git a/src/main/java/seng302/gameServer/server/messages/YachtEventCodeMessage.java b/src/main/java/seng302/gameServer/server/messages/YachtEventCodeMessage.java new file mode 100644 index 00000000..08db575a --- /dev/null +++ b/src/main/java/seng302/gameServer/server/messages/YachtEventCodeMessage.java @@ -0,0 +1,52 @@ +package seng302.gameServer.server.messages; + +/** + * Created by zyt10 on 10/08/17. + */ +public class YachtEventCodeMessage extends Message { + + private final MessageType MESSAGE_TYPE = MessageType.YACHT_EVENT_CODE; + private final int MESSAGE_VERSION = 1; //Always set to 1 + private final int MESSAGE_SIZE = 22; + + // Message fields + private long timeStamp; + private long ack = 0x00; //Unused + private int raceId; + private int destSourceId; + private int incidentId; + private int eventId; + + + public YachtEventCodeMessage(Integer subjectId) { + timeStamp = System.currentTimeMillis() / 1000L; + ack = 0; + raceId = 1; + destSourceId = subjectId; // collision boat source id + incidentId = 0; + eventId = 33; + + setHeader(new Header(MESSAGE_TYPE, 0x01, (short) getSize())); + allocateBuffer(); + writeHeaderToBuffer(); + + // Write message fields + putUnsignedByte((byte) MESSAGE_VERSION); + putInt((int) timeStamp, 6); + putInt((int) ack, 2); + putInt((int) raceId, 4); + putInt((int) destSourceId, 4); + putInt((int) incidentId, 4); + putInt((int) eventId, 1); + + writeCRC(); + rewind(); + } + + /** + * @return The length of this message + */ + public int getSize() { + return MESSAGE_SIZE; + } +} diff --git a/src/main/java/seng302/model/Yacht.java b/src/main/java/seng302/model/Yacht.java index a3a9094a..2eca35c3 100644 --- a/src/main/java/seng302/model/Yacht.java +++ b/src/main/java/seng302/model/Yacht.java @@ -16,6 +16,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Set; +import java.util.Observable; import static seng302.utilities.GeoUtility.getGeoCoordinate; @@ -25,7 +26,7 @@ import static seng302.utilities.GeoUtility.getGeoCoordinate; * Class created to store more variables (eg. boat statuses) compared to the XMLParser boat class, * also done outside Boat class because some old variables are not used anymore. */ -public class Yacht { +public class Yacht extends Observable { @FunctionalInterface public interface YachtLocationListener { @@ -148,8 +149,15 @@ public class Yacht { if (shouldDoCollisionUpdate()){ Yacht collidedYacht = checkCollision(calculatedPoint); - if (collidedYacht != null || markCollidedWith() != null) { + if (collidedYacht != null) { + location = calculateBounceBack(new GeoPoint(collidedYacht.getLocation().getLat(), + collidedYacht.getLocation().getLng())); + setChanged(); + notifyObservers(this.sourceId); + } else if (markCollidedWith() != null) { location = calculateBounceBack(new GeoPoint(markCollidedWith().getLat(), markCollidedWith().getLng())); + setChanged(); + notifyObservers(this.sourceId); } else { location = calculatedPoint; } diff --git a/src/main/java/seng302/visualiser/GameClient.java b/src/main/java/seng302/visualiser/GameClient.java index c5dd1fc9..5068889d 100644 --- a/src/main/java/seng302/visualiser/GameClient.java +++ b/src/main/java/seng302/visualiser/GameClient.java @@ -342,8 +342,8 @@ public class GameClient { * Tells race view to show a collision animation. */ private void showCollisionAlert(YachtEventData yachtEventData) { - // 1 is used by team 28 to show collision - if (yachtEventData.getEventId() == 1) { + // 33 is the agreed code to show collision + if (yachtEventData.getEventId() == 33) { raceView.showCollision(yachtEventData.getSubjectId()); } } diff --git a/src/main/java/seng302/visualiser/GameView.java b/src/main/java/seng302/visualiser/GameView.java index f13fa27c..2e6a8678 100644 --- a/src/main/java/seng302/visualiser/GameView.java +++ b/src/main/java/seng302/visualiser/GameView.java @@ -6,13 +6,16 @@ import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; import javafx.animation.Animation; import javafx.animation.AnimationTimer; import javafx.animation.KeyFrame; +import javafx.animation.KeyValue; import javafx.animation.Timeline; import javafx.application.Platform; import javafx.collections.ObservableList; import javafx.event.ActionEvent; +import javafx.event.Event; import javafx.event.EventHandler; import javafx.geometry.Point2D; import javafx.scene.Group; @@ -24,6 +27,7 @@ import javafx.scene.paint.Color; import javafx.scene.paint.Paint; import javafx.scene.shape.Circle; import javafx.scene.shape.Polygon; +import javafx.scene.shape.StrokeType; import javafx.scene.text.Text; import javafx.util.Duration; import seng302.model.Colors; @@ -616,26 +620,32 @@ public class GameView extends Pane { */ public void drawCollision(GeoPoint collisionPoint) { System.out.println("ran"); - Point2D point = findScaledXY(collisionPoint); - Circle circle = new Circle(point.getX(), point.getY(), 10.0, Color.RED); - gameObjects.add(circle); + Platform.runLater(() -> { + Point2D point = findScaledXY(collisionPoint); + double circleRadius = 0.0; + Circle circle = new Circle(point.getX(), point.getY(), circleRadius, Color.RED); + gameObjects.add(circle); - Timeline timeline = new Timeline(); - timeline.setCycleCount(1); - EventHandler blink = (ActionEvent event) -> { - if (circle.getFill() == Color.RED) { - circle.setFill(Color.TRANSPARENT); - } else { - circle.setFill(Color.RED); - } - System.out.println("beep boop"); - }; + circle.setFill(Color.TRANSPARENT); + circle.setStroke(Color.RED); + circle.setStrokeWidth(3); - KeyFrame keyframe = new KeyFrame(Duration.millis(200), blink); + Timeline timeline = new Timeline(); + timeline.setCycleCount(1); - timeline.getKeyFrames().add(keyframe); - timeline.play(); + KeyFrame keyframe1 = new KeyFrame(Duration.ZERO, + new KeyValue(circle.radiusProperty(), 0), + new KeyValue(circle.strokeProperty(), Color.TRANSPARENT)); + KeyFrame keyFrame2 = new KeyFrame(new Duration(1000), + new KeyValue(circle.radiusProperty(), 50), + new KeyValue(circle.strokeProperty(), Color.RED)); + KeyFrame keyFrame3 = new KeyFrame(new Duration(1500), + new KeyValue(circle.strokeProperty(), Color.TRANSPARENT)); -// gameObjects.remove(circle); + timeline.getKeyFrames().addAll(keyframe1, keyFrame2, keyFrame3); + timeline.play(); + + timeline.setOnFinished(event -> gameObjects.remove(circle)); + }); } }