commit
2383915c78
@ -0,0 +1,3 @@
|
|||||||
|
<component name="CopyrightManager">
|
||||||
|
<settings default="" />
|
||||||
|
</component>
|
||||||
@ -0,0 +1,89 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>team-7</artifactId>
|
||||||
|
<groupId>seng302</groupId>
|
||||||
|
<version>2.0</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
<name>matchBrowser</name>
|
||||||
|
<artifactId>matchBrowser</artifactId>
|
||||||
|
<version>2.0</version>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<version>4.12</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-all -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
<artifactId>mockito-all</artifactId>
|
||||||
|
<version>1.9.5</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.testng</groupId>
|
||||||
|
<artifactId>testng</artifactId>
|
||||||
|
<version>6.11</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>seng302</groupId>
|
||||||
|
<artifactId>racevisionGame</artifactId>
|
||||||
|
<version>2.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>1.8</maven.compiler.source>
|
||||||
|
<maven.compiler.target>1.8</maven.compiler.target>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.5.1</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-shade-plugin</artifactId>
|
||||||
|
<version>2.4.3</version>
|
||||||
|
<configuration>
|
||||||
|
<transformers>
|
||||||
|
<transformer
|
||||||
|
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||||
|
<manifestEntries>
|
||||||
|
<Main-Class>app.Main</Main-Class>
|
||||||
|
<X-Compile-Source-JDK>${maven.compiler.source}</X-Compile-Source-JDK>
|
||||||
|
<X-Compile-Target-JDK>${maven.compiler.target}</X-Compile-Target-JDK>
|
||||||
|
</manifestEntries>
|
||||||
|
</transformer>
|
||||||
|
</transformers>
|
||||||
|
</configuration>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>shade</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
package app;
|
||||||
|
|
||||||
|
import networkInterface.NetworkInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used when starting the matchmaking browser
|
||||||
|
*/
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
NetworkInterface networkInterface = new NetworkInterface();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
package model;
|
||||||
|
|
||||||
|
public class ClientAddress {
|
||||||
|
private String ip;
|
||||||
|
private int port;
|
||||||
|
|
||||||
|
public ClientAddress(String ip, int port) {
|
||||||
|
this.ip = ip;
|
||||||
|
this.port = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIp() {
|
||||||
|
return ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPort() {
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
return o != null && o instanceof ClientAddress && hashCode() == o.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = ip != null ? ip.hashCode() : 0;
|
||||||
|
result *= 31;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "{ip='" + ip + '\'' +
|
||||||
|
", port=" + port+"}";
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
package model;
|
||||||
|
|
||||||
|
|
||||||
|
import network.Messages.HostGame;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds a table object that stores current games
|
||||||
|
*/
|
||||||
|
public class MatchTable {
|
||||||
|
private HashMap<ClientAddress, HostGame> matchTable;
|
||||||
|
|
||||||
|
public MatchTable() {
|
||||||
|
this.matchTable = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addEntry(ClientAddress address, HostGame newEntry) {
|
||||||
|
this.matchTable.put(address, newEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HashMap<ClientAddress, HostGame> getMatchTable() {
|
||||||
|
return matchTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "MatchTable=" + matchTable;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
package model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to create a key made of an ip and port.
|
||||||
|
*/
|
||||||
|
public class TableKey {
|
||||||
|
|
||||||
|
private final String ip;
|
||||||
|
private final int port;
|
||||||
|
|
||||||
|
public TableKey(String ip, int port) {
|
||||||
|
this.ip = ip;
|
||||||
|
this.port = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (!(o instanceof TableKey)) return false;
|
||||||
|
TableKey key = (TableKey) o;
|
||||||
|
return ip.equals(key.ip) && port == key.port;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = port;
|
||||||
|
result = 31 * result + ip.hashCode();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "[ip='" + ip + '\'' +
|
||||||
|
", port=" + port +
|
||||||
|
']';
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,132 @@
|
|||||||
|
package networkInterface;
|
||||||
|
|
||||||
|
import model.ClientAddress;
|
||||||
|
import model.MatchTable;
|
||||||
|
import network.BinaryMessageDecoder;
|
||||||
|
import network.Exceptions.InvalidMessageException;
|
||||||
|
import network.MessageDecoders.HostGameMessageDecoder;
|
||||||
|
import network.MessageDecoders.HostedGamesRequestDecoder;
|
||||||
|
import network.MessageEncoders.HostedGamesRequestEncoder;
|
||||||
|
import network.Messages.Enums.MessageType;
|
||||||
|
import network.Messages.HostGame;
|
||||||
|
import network.Messages.HostGamesRequest;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.DatagramPacket;
|
||||||
|
import java.net.DatagramSocket;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds the output for the network for
|
||||||
|
*/
|
||||||
|
public class NetworkInterface {
|
||||||
|
private Timer scheduler;
|
||||||
|
private DatagramSocket serverSocket;
|
||||||
|
private byte[] receiveData = new byte[1024];
|
||||||
|
|
||||||
|
private Set<ClientAddress> clientsAddresses;
|
||||||
|
private MatchTable matchTable;
|
||||||
|
|
||||||
|
|
||||||
|
public NetworkInterface(){
|
||||||
|
this.clientsAddresses = new HashSet<>();
|
||||||
|
this.matchTable = new MatchTable();
|
||||||
|
this.scheduler = new Timer(true);
|
||||||
|
try {
|
||||||
|
this.serverSocket = new DatagramSocket(3779);
|
||||||
|
|
||||||
|
startBroadcast(5000);
|
||||||
|
scheduleFlush(70000);
|
||||||
|
this.run();
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.err.println("Error listening on port: " + this.serverSocket.getLocalPort() + ".");
|
||||||
|
System.exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Broadcasts match table to clients at a requested interval
|
||||||
|
* @param period interval to broadcast table
|
||||||
|
*/
|
||||||
|
private void startBroadcast(int period) {
|
||||||
|
scheduler.scheduleAtFixedRate(new TimerTask() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
List<HostGame> games = new ArrayList<>();
|
||||||
|
|
||||||
|
for(Map.Entry<ClientAddress, HostGame> tableEntry: matchTable.getMatchTable().entrySet()) {
|
||||||
|
HostGame game = tableEntry.getValue();
|
||||||
|
if(game != null) {
|
||||||
|
games.add(game);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HostedGamesRequestEncoder encoder = new HostedGamesRequestEncoder();
|
||||||
|
try {
|
||||||
|
byte[] message = encoder.encode(new HostGamesRequest(games));
|
||||||
|
for(ClientAddress address: clientsAddresses) {
|
||||||
|
serverSocket.send(new DatagramPacket(message, message.length, InetAddress.getByName(address.getIp()), 4941));
|
||||||
|
}
|
||||||
|
} catch (InvalidMessageException | IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, period, period);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flushes the match table at a requested interval
|
||||||
|
* @param period interval to flush table
|
||||||
|
*/
|
||||||
|
private void scheduleFlush(int period) {
|
||||||
|
scheduler.scheduleAtFixedRate(new TimerTask() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
matchTable.getMatchTable().clear();
|
||||||
|
}
|
||||||
|
}, period, period);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void run() throws IOException{
|
||||||
|
while(true) {
|
||||||
|
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
|
||||||
|
serverSocket.receive(receivePacket);
|
||||||
|
|
||||||
|
BinaryMessageDecoder messageDecoder = new BinaryMessageDecoder(receivePacket.getData());
|
||||||
|
switch (MessageType.fromByte(messageDecoder.getHeaderMessageType())){
|
||||||
|
case HOST_GAME:
|
||||||
|
//decode and update table
|
||||||
|
HostGameMessageDecoder decoder = new HostGameMessageDecoder();
|
||||||
|
HostGame newKnownGame;
|
||||||
|
try{
|
||||||
|
newKnownGame = (HostGame) decoder.decode(messageDecoder.getMessageBody());
|
||||||
|
newKnownGame.setIp(receivePacket.getAddress().getHostAddress());
|
||||||
|
this.matchTable.addEntry(new ClientAddress(receivePacket.getAddress().getHostAddress(), receivePacket.getPort()), newKnownGame);
|
||||||
|
|
||||||
|
}catch (InvalidMessageException e){
|
||||||
|
System.out.println(e);
|
||||||
|
System.err.println("Message received that is not a hostedGame packet");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case HOSTED_GAMES_REQUEST:
|
||||||
|
//update known clients
|
||||||
|
HostedGamesRequestDecoder decoder2 = new HostedGamesRequestDecoder();
|
||||||
|
HostGamesRequest newKnownGames;
|
||||||
|
try{
|
||||||
|
newKnownGames = (HostGamesRequest) decoder2.decode(messageDecoder.getMessageBody());
|
||||||
|
if (newKnownGames.getKnownGames().size() == 0){
|
||||||
|
//this is just an alert message with no content
|
||||||
|
clientsAddresses.add(new ClientAddress(receivePacket.getAddress().getHostAddress(), receivePacket.getPort()));
|
||||||
|
}
|
||||||
|
}catch (InvalidMessageException e){
|
||||||
|
System.out.println(e);
|
||||||
|
System.err.println("Message received that is not a hostedGamesRequest packet");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
package model;
|
||||||
|
|
||||||
|
import network.Messages.Enums.RaceStatusEnum;
|
||||||
|
import network.Messages.HostGame;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class MatchTableTest {
|
||||||
|
private MatchTable testTable;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
testTable = new MatchTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTable() {
|
||||||
|
HostGame entry = new HostGame("127.0.0.1", 4942, (byte)1, (byte)1, RaceStatusEnum.PRESTART, (byte)6, (byte)1);
|
||||||
|
|
||||||
|
testTable.addEntry(new ClientAddress("127.0.0.1", 3779), entry);
|
||||||
|
|
||||||
|
assertEquals(testTable.getMatchTable().get(new ClientAddress("127.0.0.1", 4942)), entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
package mock.model.commandFactory;
|
||||||
|
|
||||||
|
import java.util.Observable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to track the current active observer command. This is to ensure two commands that do similar things do not overlap.
|
||||||
|
*/
|
||||||
|
public class ActiveObserverCommand {
|
||||||
|
private ObserverCommand currentVelocityCommand;
|
||||||
|
private ObserverCommand currentAngularCommand;
|
||||||
|
|
||||||
|
public ActiveObserverCommand() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void changeVelocityCommand(Observable o, ObserverCommand c) {
|
||||||
|
o.deleteObserver(currentVelocityCommand);
|
||||||
|
o.addObserver(c);
|
||||||
|
currentVelocityCommand = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void changeAngularCommand(Observable o, ObserverCommand c) {
|
||||||
|
o.deleteObserver(currentAngularCommand);
|
||||||
|
o.addObserver(c);
|
||||||
|
currentAngularCommand = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,45 @@
|
|||||||
|
package mock.model.commandFactory;
|
||||||
|
|
||||||
|
import mock.model.MockBoat;
|
||||||
|
import mock.model.MockRace;
|
||||||
|
import shared.model.Azimuth;
|
||||||
|
import shared.model.GPSCoordinate;
|
||||||
|
|
||||||
|
import java.util.Observable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command class for collisions
|
||||||
|
*/
|
||||||
|
public class CollisionCommand extends ObserverCommand {
|
||||||
|
private GPSCoordinate startingPosition;
|
||||||
|
private Azimuth azimuth;
|
||||||
|
private double distance;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for class
|
||||||
|
* @param race race context
|
||||||
|
* @param boat boat controlled by command
|
||||||
|
*/
|
||||||
|
public CollisionCommand(MockRace race, MockBoat boat) {
|
||||||
|
super(race, boat);
|
||||||
|
race.addObserver(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() {
|
||||||
|
this.azimuth = Azimuth.fromDegrees(boat.getBearing().degrees() - 180d);
|
||||||
|
this.startingPosition = boat.getPosition();
|
||||||
|
this.distance = 30;
|
||||||
|
boat.setVelocityDefault(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(Observable o, Object arg) {
|
||||||
|
if(GPSCoordinate.calculateDistanceMeters(boat.getPosition(), startingPosition) < distance) {
|
||||||
|
boat.setPosition(GPSCoordinate.calculateNewPosition(boat.getPosition(), 2, azimuth));
|
||||||
|
} else {
|
||||||
|
race.deleteObserver(this);
|
||||||
|
boat.setVelocityDefault(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
package mock.model.commandFactory;
|
||||||
|
|
||||||
|
import mock.model.MockBoat;
|
||||||
|
import mock.model.MockRace;
|
||||||
|
|
||||||
|
import java.util.Observer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command that can observe the race
|
||||||
|
*/
|
||||||
|
public abstract class ObserverCommand implements Command, Observer {
|
||||||
|
MockRace race;
|
||||||
|
MockBoat boat;
|
||||||
|
|
||||||
|
public ObserverCommand(MockRace race, MockBoat boat) {
|
||||||
|
this.race = race;
|
||||||
|
this.boat = boat;
|
||||||
|
boat.setAutoVMG(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,64 @@
|
|||||||
|
package network.MessageDecoders;
|
||||||
|
|
||||||
|
import network.Exceptions.InvalidMessageException;
|
||||||
|
import network.Messages.AC35Data;
|
||||||
|
import network.Messages.CourseWinds;
|
||||||
|
import network.Messages.Enums.RaceStatusEnum;
|
||||||
|
import network.Messages.HostGame;
|
||||||
|
import network.Messages.RaceStatus;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import static network.Utils.ByteConverter.bytesToInt;
|
||||||
|
import static network.Utils.ByteConverter.bytesToLong;
|
||||||
|
|
||||||
|
public class HostGameMessageDecoder implements MessageDecoder {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The encoded message.
|
||||||
|
*/
|
||||||
|
private byte[] encodedMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The decoded message.
|
||||||
|
*/
|
||||||
|
private HostGame message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
public HostGameMessageDecoder() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AC35Data decode(byte[] encodedMessage) throws InvalidMessageException {
|
||||||
|
this.encodedMessage = encodedMessage;
|
||||||
|
|
||||||
|
try{
|
||||||
|
byte ipPart1 = encodedMessage[0];
|
||||||
|
byte ipPart2 = encodedMessage[1];
|
||||||
|
byte ipPart3 = encodedMessage[2];
|
||||||
|
byte ipPart4 = encodedMessage[3];
|
||||||
|
String ipString = bytesToLong(ipPart1) + "." + bytesToLong(ipPart2) + "." + bytesToLong(ipPart3) + "." + bytesToLong(ipPart4);
|
||||||
|
// System.out.println(ipString);
|
||||||
|
int port = bytesToInt(Arrays.copyOfRange(encodedMessage, 4, 8));
|
||||||
|
byte map = encodedMessage[8];
|
||||||
|
byte speed = encodedMessage[9];
|
||||||
|
byte status = encodedMessage[10];
|
||||||
|
byte requiredNumPlayers = encodedMessage[11];
|
||||||
|
byte currentNumPlayers = encodedMessage[12];
|
||||||
|
|
||||||
|
|
||||||
|
message = new HostGame(ipString, port, map,
|
||||||
|
speed, RaceStatusEnum.fromByte(status),
|
||||||
|
requiredNumPlayers, currentNumPlayers);
|
||||||
|
|
||||||
|
return message;
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new InvalidMessageException("Could not decode Host game message.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
package network.MessageDecoders;
|
||||||
|
|
||||||
|
import network.Exceptions.InvalidMessageException;
|
||||||
|
import network.Messages.AC35Data;
|
||||||
|
import network.Messages.HostGame;
|
||||||
|
import network.Messages.HostGamesRequest;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static network.Utils.ByteConverter.bytesToInt;
|
||||||
|
|
||||||
|
public class HostedGamesRequestDecoder implements MessageDecoder{
|
||||||
|
@Override
|
||||||
|
public AC35Data decode(byte[] encodedMessage) throws InvalidMessageException {
|
||||||
|
try{
|
||||||
|
int numberOfGames = bytesToInt(Arrays.copyOfRange(encodedMessage, 0, 4));
|
||||||
|
|
||||||
|
HostGameMessageDecoder lineDecoder = new HostGameMessageDecoder();
|
||||||
|
List<HostGame> knownGames = new ArrayList<>();
|
||||||
|
int byteIndex = 4;
|
||||||
|
for (int i = 0; i < numberOfGames; i++){
|
||||||
|
knownGames.add((HostGame) lineDecoder.decode(Arrays.copyOfRange(encodedMessage, byteIndex, byteIndex+13)));
|
||||||
|
byteIndex += 13;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new HostGamesRequest(knownGames);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
throw new InvalidMessageException("Could not decode Host game message.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,52 @@
|
|||||||
|
package network.MessageEncoders;
|
||||||
|
|
||||||
|
import network.Exceptions.InvalidMessageException;
|
||||||
|
import network.Messages.AC35Data;
|
||||||
|
import network.Messages.HostGame;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import static network.Utils.ByteConverter.intToBytes;
|
||||||
|
|
||||||
|
|
||||||
|
public class HostGameMessageEncoder implements MessageEncoder{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
public HostGameMessageEncoder() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] encode(AC35Data message) throws InvalidMessageException {
|
||||||
|
try{
|
||||||
|
//Downcast
|
||||||
|
HostGame hostGame = (HostGame) message;
|
||||||
|
|
||||||
|
ByteBuffer hostGameMessage = ByteBuffer.allocate(13);
|
||||||
|
|
||||||
|
ByteBuffer ipBytes = ByteBuffer.allocate(4);
|
||||||
|
String ip = hostGame.getIp();
|
||||||
|
String[] ipValues = ip.split("\\.");
|
||||||
|
for(String value:ipValues){
|
||||||
|
ipBytes.put(intToBytes(Integer.parseInt(value), 1)[0]);
|
||||||
|
}
|
||||||
|
byte raceStatus = hostGame.getStatus().getValue();
|
||||||
|
|
||||||
|
hostGameMessage.put(ipBytes.array());
|
||||||
|
hostGameMessage.put(intToBytes(hostGame.getPort()));
|
||||||
|
hostGameMessage.put(hostGame.getMap());
|
||||||
|
hostGameMessage.put(hostGame.getSpeed());
|
||||||
|
hostGameMessage.put(raceStatus);
|
||||||
|
hostGameMessage.put(hostGame.getRequiredNumPlayers());
|
||||||
|
hostGameMessage.put(hostGame.getCurrentNumPlayers());
|
||||||
|
|
||||||
|
|
||||||
|
// System.out.println(hostGameMessage.array()[4]);
|
||||||
|
return hostGameMessage.array();
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new InvalidMessageException("Could not encode Host game message.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,43 @@
|
|||||||
|
package network.MessageEncoders;
|
||||||
|
|
||||||
|
import network.Exceptions.InvalidMessageException;
|
||||||
|
import network.Messages.AC35Data;
|
||||||
|
import network.Messages.HostGame;
|
||||||
|
import network.Messages.HostGamesRequest;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import static network.Utils.ByteConverter.intToBytes;
|
||||||
|
|
||||||
|
public class HostedGamesRequestEncoder implements MessageEncoder{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
public HostedGamesRequestEncoder() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] encode(AC35Data message) throws InvalidMessageException {
|
||||||
|
try{
|
||||||
|
//Downcast
|
||||||
|
HostGamesRequest hostGamesRequest = (HostGamesRequest) message;
|
||||||
|
|
||||||
|
int numGames = hostGamesRequest.getKnownGames().size();
|
||||||
|
|
||||||
|
ByteBuffer hostedGamesRequestMessage = ByteBuffer.allocate(4+13*numGames);
|
||||||
|
|
||||||
|
hostedGamesRequestMessage.put(intToBytes(numGames));
|
||||||
|
|
||||||
|
HostGameMessageEncoder lineEncoder = new HostGameMessageEncoder();
|
||||||
|
for (HostGame line: hostGamesRequest.getKnownGames()) {
|
||||||
|
hostedGamesRequestMessage.put(lineEncoder.encode(line));
|
||||||
|
}
|
||||||
|
|
||||||
|
return hostedGamesRequestMessage.array();
|
||||||
|
|
||||||
|
}catch(Exception e){
|
||||||
|
throw new InvalidMessageException("Could not encode Host game message.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,89 @@
|
|||||||
|
package network.Messages;
|
||||||
|
|
||||||
|
|
||||||
|
import network.Messages.Enums.MessageType;
|
||||||
|
import network.Messages.Enums.RaceStatusEnum;
|
||||||
|
import network.Utils.ByteConverter;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import static network.Utils.ByteConverter.intToBytes;
|
||||||
|
|
||||||
|
public class HostGame extends AC35Data {
|
||||||
|
|
||||||
|
private String ip;
|
||||||
|
private int port;
|
||||||
|
private byte map;
|
||||||
|
private byte speed;
|
||||||
|
private RaceStatusEnum status;
|
||||||
|
private byte requiredNumPlayers;
|
||||||
|
private byte currentNumPlayers;
|
||||||
|
|
||||||
|
public HostGame(String ip, int port, byte map, byte speed,
|
||||||
|
RaceStatusEnum status, byte requiredNumPlayers,
|
||||||
|
byte currentNumPlayers) {
|
||||||
|
super(MessageType.HOST_GAME);
|
||||||
|
this.ip = ip;
|
||||||
|
this.port = port;
|
||||||
|
this.map = map;
|
||||||
|
this.speed = speed;
|
||||||
|
this.status = status;
|
||||||
|
this.requiredNumPlayers = requiredNumPlayers;
|
||||||
|
this.currentNumPlayers = currentNumPlayers;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the ip of host
|
||||||
|
*/
|
||||||
|
public String getIp() {
|
||||||
|
return ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the port of host
|
||||||
|
*/
|
||||||
|
public int getPort() {
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the map index
|
||||||
|
*/
|
||||||
|
public byte getMap() {
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the speed value of game
|
||||||
|
*/
|
||||||
|
public byte getSpeed() {
|
||||||
|
return speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the status of race
|
||||||
|
*/
|
||||||
|
public RaceStatusEnum getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return required number of players
|
||||||
|
*/
|
||||||
|
public byte getRequiredNumPlayers() {
|
||||||
|
return requiredNumPlayers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return current number of players
|
||||||
|
*/
|
||||||
|
public byte getCurrentNumPlayers() {
|
||||||
|
return currentNumPlayers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIp(String ip) {
|
||||||
|
this.ip = ip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
package network.Messages;
|
||||||
|
|
||||||
|
import network.Messages.Enums.MessageType;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class HostGamesRequest extends AC35Data{
|
||||||
|
|
||||||
|
private List<HostGame> knownGames;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param knownGames games known by sender
|
||||||
|
*/
|
||||||
|
public HostGamesRequest(List knownGames) {
|
||||||
|
super(MessageType.HOSTED_GAMES_REQUEST);
|
||||||
|
this.knownGames = knownGames;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<HostGame> getKnownGames() {
|
||||||
|
return knownGames;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,118 @@
|
|||||||
|
package shared.model;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains data related to mark rounding for a specific leg.
|
||||||
|
*/
|
||||||
|
public class MarkRoundingData {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The leg this relates to.
|
||||||
|
*/
|
||||||
|
private Leg leg;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The mark that should be rounded.
|
||||||
|
*/
|
||||||
|
private Mark markToRound;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The bearing of the leg.
|
||||||
|
*/
|
||||||
|
private Bearing legBearing;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The bearing of the next leg.
|
||||||
|
*/
|
||||||
|
private Bearing nextLegBearing;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The location of the first rounding check point.
|
||||||
|
*/
|
||||||
|
private GPSCoordinate roundCheck1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The location of the second rounding check point.
|
||||||
|
*/
|
||||||
|
private GPSCoordinate roundCheck2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A halfway point between mark to round and roundCheck1.
|
||||||
|
*/
|
||||||
|
private GPSCoordinate roundCheck1Halfway;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A halfway point between mark to round and roundCheck2.
|
||||||
|
*/
|
||||||
|
private GPSCoordinate roundCheck2Halfway;
|
||||||
|
|
||||||
|
|
||||||
|
public MarkRoundingData() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Leg getLeg() {
|
||||||
|
return leg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLeg(Leg leg) {
|
||||||
|
this.leg = leg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mark getMarkToRound() {
|
||||||
|
return markToRound;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMarkToRound(Mark markToRound) {
|
||||||
|
this.markToRound = markToRound;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bearing getLegBearing() {
|
||||||
|
return legBearing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLegBearing(Bearing legBearing) {
|
||||||
|
this.legBearing = legBearing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bearing getNextLegBearing() {
|
||||||
|
return nextLegBearing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNextLegBearing(Bearing nextLegBearing) {
|
||||||
|
this.nextLegBearing = nextLegBearing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GPSCoordinate getRoundCheck1() {
|
||||||
|
return roundCheck1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRoundCheck1(GPSCoordinate roundCheck1) {
|
||||||
|
this.roundCheck1 = roundCheck1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GPSCoordinate getRoundCheck2() {
|
||||||
|
return roundCheck2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRoundCheck2(GPSCoordinate roundCheck2) {
|
||||||
|
this.roundCheck2 = roundCheck2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GPSCoordinate getRoundCheck1Halfway() {
|
||||||
|
return roundCheck1Halfway;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRoundCheck1Halfway(GPSCoordinate roundCheck1Halfway) {
|
||||||
|
this.roundCheck1Halfway = roundCheck1Halfway;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GPSCoordinate getRoundCheck2Halfway() {
|
||||||
|
return roundCheck2Halfway;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRoundCheck2Halfway(GPSCoordinate roundCheck2Halfway) {
|
||||||
|
this.roundCheck2Halfway = roundCheck2Halfway;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,210 @@
|
|||||||
|
package shared.model;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import static shared.enums.RoundingType.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class contains a sequence of points that describe the mark rounding order for a course.
|
||||||
|
*/
|
||||||
|
public class MarkRoundingSequence {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Legs in the race.
|
||||||
|
*/
|
||||||
|
private List<Leg> legs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For each leg, mark rounding information.
|
||||||
|
*/
|
||||||
|
private Map<Leg, MarkRoundingData> roundingPoints;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public MarkRoundingSequence(List<Leg> legs) {
|
||||||
|
this.legs = legs;
|
||||||
|
generateRoundingPoints();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the rounding points for a given leg.
|
||||||
|
* @param leg Leg to check.
|
||||||
|
* @return Rounding points for leg.
|
||||||
|
*/
|
||||||
|
public MarkRoundingData getRoundingData(Leg leg) {
|
||||||
|
return roundingPoints.get(leg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the rounding points for all legs in the race.
|
||||||
|
*/
|
||||||
|
private void generateRoundingPoints() {
|
||||||
|
this.roundingPoints = new HashMap<>(this.legs.size());
|
||||||
|
|
||||||
|
for (int i = 0; i < this.legs.size(); i++) {
|
||||||
|
Leg currentLeg = this.legs.get(i);
|
||||||
|
|
||||||
|
Optional<Leg> nextLeg = Optional.empty();
|
||||||
|
if (i < legs.size() - 1) {
|
||||||
|
nextLeg = Optional.of(this.legs.get(i + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
generateRoundingPoint(currentLeg, nextLeg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the rounding points for a specific leg.
|
||||||
|
* @param currentLeg The leg to generate rounding points for.
|
||||||
|
* @param nextLeg The following leg, used to help generate rounding points. Final leg of race doesn't have a following leg.
|
||||||
|
*/
|
||||||
|
private void generateRoundingPoint(Leg currentLeg, Optional<Leg> nextLeg) {
|
||||||
|
|
||||||
|
Bearing bearingToAddFirstPoint = calculateBearingToAdd(currentLeg);
|
||||||
|
|
||||||
|
GPSCoordinate startCoord = currentLeg.getStartCompoundMark().getAverageGPSCoordinate();
|
||||||
|
GPSCoordinate endCoord = currentLeg.getEndCompoundMark().getAverageGPSCoordinate();
|
||||||
|
Bearing legBearing = GPSCoordinate.calculateBearing(startCoord, endCoord);
|
||||||
|
Bearing nextBearing = legBearing;
|
||||||
|
|
||||||
|
Mark markToRound = currentLeg.getEndCompoundMark().getMarkForRounding(legBearing);
|
||||||
|
|
||||||
|
GPSCoordinate roundCheck1;
|
||||||
|
if (currentLeg.getEndCompoundMark().getMark2() == null) {
|
||||||
|
//End is a single mark.
|
||||||
|
roundCheck1 = calculateRoundingCheckPoint(
|
||||||
|
currentLeg,
|
||||||
|
markToRound,
|
||||||
|
legBearing,
|
||||||
|
bearingToAddFirstPoint);
|
||||||
|
} else {
|
||||||
|
//End is a gate.
|
||||||
|
if (markToRound == currentLeg.getEndCompoundMark().getMark1()) {
|
||||||
|
roundCheck1 = currentLeg.getEndCompoundMark().getMark2().getPosition();
|
||||||
|
} else {
|
||||||
|
roundCheck1 = currentLeg.getEndCompoundMark().getMark1().getPosition();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO the halfway points currently haven't been done properly.
|
||||||
|
|
||||||
|
GPSCoordinate roundCheck1Halfway = calculateRoundingCheckPoint(
|
||||||
|
currentLeg,
|
||||||
|
markToRound,
|
||||||
|
legBearing,
|
||||||
|
bearingToAddFirstPoint);
|
||||||
|
|
||||||
|
|
||||||
|
GPSCoordinate roundCheck2 = roundCheck1;
|
||||||
|
GPSCoordinate roundCheck2Halfway = roundCheck1Halfway;
|
||||||
|
if (nextLeg.isPresent()) {
|
||||||
|
|
||||||
|
Bearing bearingToAddSecondPoint = bearingToAddFirstPoint;//calculateBearingToAdd(nextLeg.get());
|
||||||
|
|
||||||
|
GPSCoordinate startCoord2 = nextLeg.get().getStartCompoundMark().getAverageGPSCoordinate();
|
||||||
|
GPSCoordinate endCoord2 = nextLeg.get().getEndCompoundMark().getAverageGPSCoordinate();
|
||||||
|
nextBearing = GPSCoordinate.calculateBearing(startCoord2, endCoord2);
|
||||||
|
|
||||||
|
roundCheck2 = calculateRoundingCheckPoint(
|
||||||
|
currentLeg,
|
||||||
|
markToRound,
|
||||||
|
nextBearing,
|
||||||
|
bearingToAddSecondPoint);
|
||||||
|
|
||||||
|
roundCheck2Halfway = calculateRoundingCheckPoint(
|
||||||
|
currentLeg,
|
||||||
|
markToRound,
|
||||||
|
nextBearing,
|
||||||
|
bearingToAddSecondPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
MarkRoundingData roundingData = new MarkRoundingData();
|
||||||
|
roundingData.setLeg(currentLeg);
|
||||||
|
|
||||||
|
roundingData.setLegBearing(legBearing);
|
||||||
|
roundingData.setNextLegBearing(nextBearing);
|
||||||
|
|
||||||
|
roundingData.setMarkToRound(markToRound);
|
||||||
|
|
||||||
|
roundingData.setRoundCheck1(roundCheck1);
|
||||||
|
roundingData.setRoundCheck1Halfway(roundCheck1Halfway);
|
||||||
|
|
||||||
|
roundingData.setRoundCheck2(roundCheck2);
|
||||||
|
roundingData.setRoundCheck2Halfway(roundCheck2Halfway);
|
||||||
|
|
||||||
|
|
||||||
|
this.roundingPoints.put(currentLeg, roundingData);
|
||||||
|
|
||||||
|
|
||||||
|
//Rounding points:
|
||||||
|
|
||||||
|
//each mark/gate has a specific mark to round. Call this ROUNDINGMARK
|
||||||
|
// with a mark, it is the mark
|
||||||
|
// with a gate, it depends if it is a starboard or port gate.
|
||||||
|
// it is the mark that allows the boat to enter between both marks of the gate, whilst obeying the starboard/port requirement.
|
||||||
|
|
||||||
|
//let the bearing between start of leg and end of leg be called LEGBEARING
|
||||||
|
|
||||||
|
//the first rounding point is ROUNDINGDISTANCE units away from the ROUNDINGMARK, on an angle perpendicular to LEGBEARING.
|
||||||
|
// the angle means that the rounding mark is at the center of a gate, for gates.
|
||||||
|
|
||||||
|
//the second rounding point is the same as the first, except LEGBEARING is the bearing between end of current leg, and start of next leg.
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the location of the rounding check point, which together with the mark to round, forms a line that the boat must cross to round the mark.
|
||||||
|
* @param leg Leg of race to check.
|
||||||
|
* @param markToRound Mark at end of leg to round.
|
||||||
|
* @param legBearing The bearing of the nearest leg. For the first rounding point this is the leg's bearing, for the second rounding point it is the next leg's bearing.
|
||||||
|
* @param bearingToAdd The bearing to add to the leg bearing to get a perpendicular bearing.
|
||||||
|
* @return The location of the rounding point, which together with the mark to round forms a line the boat must cross.
|
||||||
|
*/
|
||||||
|
private GPSCoordinate calculateRoundingCheckPoint(Leg leg, Mark markToRound, Bearing legBearing, Bearing bearingToAdd) {
|
||||||
|
|
||||||
|
|
||||||
|
double roundingDistanceMeters = leg.getEndCompoundMark().getRoundingDistance();
|
||||||
|
|
||||||
|
|
||||||
|
//We project from rounding mark to get the second point which forms the line the boat must cross.
|
||||||
|
/*
|
||||||
|
c2
|
||||||
|
|
|
||||||
|
|
|
||||||
|
r------c1
|
||||||
|
b
|
||||||
|
*/
|
||||||
|
GPSCoordinate roundCheck = GPSCoordinate.calculateNewPosition(
|
||||||
|
markToRound.getPosition(),
|
||||||
|
roundingDistanceMeters,
|
||||||
|
Azimuth.fromDegrees(legBearing.degrees() + bearingToAdd.degrees()) );
|
||||||
|
|
||||||
|
return roundCheck;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the bearing that must be added to a leg's bearing to calculate a perpendicular bearing, used for finding rounding points.
|
||||||
|
* @param leg Leg to check.
|
||||||
|
* @return Bearing to add. Will be either +90 or -90.
|
||||||
|
*/
|
||||||
|
private Bearing calculateBearingToAdd(Leg leg) {
|
||||||
|
|
||||||
|
if (leg.getEndCompoundMark().getRoundingType() == Port ||
|
||||||
|
leg.getEndCompoundMark().getRoundingType() == SP) {
|
||||||
|
return Bearing.fromDegrees(90);
|
||||||
|
} else {
|
||||||
|
return Bearing.fromDegrees(-90);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,147 +0,0 @@
|
|||||||
package visualiser.Controllers;
|
|
||||||
|
|
||||||
import javafx.collections.FXCollections;
|
|
||||||
import javafx.collections.ObservableList;
|
|
||||||
import javafx.fxml.FXML;
|
|
||||||
import javafx.scene.control.*;
|
|
||||||
import javafx.scene.layout.AnchorPane;
|
|
||||||
import mock.app.Event;
|
|
||||||
import org.xml.sax.SAXException;
|
|
||||||
import shared.exceptions.InvalidBoatDataException;
|
|
||||||
import shared.exceptions.InvalidRaceDataException;
|
|
||||||
import shared.exceptions.InvalidRegattaDataException;
|
|
||||||
import shared.exceptions.XMLReaderException;
|
|
||||||
import visualiser.model.RaceConnection;
|
|
||||||
|
|
||||||
import javax.xml.bind.JAXBException;
|
|
||||||
import javax.xml.parsers.ParserConfigurationException;
|
|
||||||
import javax.xml.soap.Text;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.Socket;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.net.UnknownHostException;
|
|
||||||
import java.util.ResourceBundle;
|
|
||||||
|
|
||||||
//TODO it appears this view/controller was replaced by Lobby.fxml. Remove?
|
|
||||||
/**
|
|
||||||
* Controls the connection that the VIsualiser can connect to.
|
|
||||||
*/
|
|
||||||
public class ConnectionController extends Controller {
|
|
||||||
@FXML
|
|
||||||
private AnchorPane connectionWrapper;
|
|
||||||
@FXML
|
|
||||||
private TableView<RaceConnection> connectionTable;
|
|
||||||
@FXML
|
|
||||||
private TableColumn<RaceConnection, String> hostnameColumn;
|
|
||||||
@FXML
|
|
||||||
private TableColumn<RaceConnection, String> statusColumn;
|
|
||||||
@FXML
|
|
||||||
private Button connectButton;
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
private TextField urlField;
|
|
||||||
@FXML
|
|
||||||
private TextField portField;
|
|
||||||
|
|
||||||
|
|
||||||
/*Title Screen fxml items*/
|
|
||||||
@FXML
|
|
||||||
private Button hostGameTitleBtn;
|
|
||||||
@FXML
|
|
||||||
private Button connectGameBtn;
|
|
||||||
@FXML
|
|
||||||
private RadioButton nightRadioBtn;
|
|
||||||
@FXML
|
|
||||||
private RadioButton dayRadioButton;
|
|
||||||
|
|
||||||
/*Lobby fxml items*/
|
|
||||||
@FXML
|
|
||||||
private TableView lobbyTable;
|
|
||||||
@FXML
|
|
||||||
private TableColumn gameNameColumn;
|
|
||||||
@FXML
|
|
||||||
private TableColumn hostNameColumn;
|
|
||||||
@FXML
|
|
||||||
private TableColumn playerCountColumn;
|
|
||||||
@FXML
|
|
||||||
private TextField playerNameField;
|
|
||||||
@FXML
|
|
||||||
private Button joinGameBtn;
|
|
||||||
|
|
||||||
/*Host game fxml items*/
|
|
||||||
@FXML
|
|
||||||
private TextField gameNameField;
|
|
||||||
@FXML
|
|
||||||
private TextField hostNameField;
|
|
||||||
@FXML
|
|
||||||
private TextField hostGameBtn;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private ObservableList<RaceConnection> connections;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initialize(URL location, ResourceBundle resources) {
|
|
||||||
// TODO - replace with config file
|
|
||||||
connections = FXCollections.observableArrayList();
|
|
||||||
|
|
||||||
connectionTable.setItems(connections);
|
|
||||||
hostnameColumn.setCellValueFactory(cellData -> cellData.getValue().hostnameProperty());
|
|
||||||
statusColumn.setCellValueFactory(cellData -> cellData.getValue().statusProperty());
|
|
||||||
|
|
||||||
connectionTable.getSelectionModel().selectedItemProperty().addListener((obs, prev, curr) -> {
|
|
||||||
if (curr != null && curr.check()) connectButton.setDisable(false);
|
|
||||||
else connectButton.setDisable(true);
|
|
||||||
});
|
|
||||||
connectButton.setDisable(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets current status of all connections.
|
|
||||||
*/
|
|
||||||
public void checkConnections() {
|
|
||||||
for(RaceConnection connection: connections) {
|
|
||||||
connection.check();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public AnchorPane startWrapper(){
|
|
||||||
return connectionWrapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Connects to host currently selected in table. Button enabled only if host is ready.
|
|
||||||
*/
|
|
||||||
public void connectSocket() {
|
|
||||||
try{
|
|
||||||
RaceConnection connection = connectionTable.getSelectionModel().getSelectedItem();
|
|
||||||
Socket socket = new Socket(connection.getHostname(), connection.getPort());
|
|
||||||
socket.setKeepAlive(true);
|
|
||||||
connectionWrapper.setVisible(false);
|
|
||||||
//parent.enterLobby(socket);
|
|
||||||
} catch (IOException e) { /* Never reached */ }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* adds a new connection
|
|
||||||
*/
|
|
||||||
public void addConnection(){
|
|
||||||
String hostName = urlField.getText();
|
|
||||||
String portString = portField.getText();
|
|
||||||
try{
|
|
||||||
int port = Integer.parseInt(portString);
|
|
||||||
connections.add(new RaceConnection(hostName, port, null));
|
|
||||||
}catch(NumberFormatException e){
|
|
||||||
System.err.println("Port number entered is not a number");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,32 +1,108 @@
|
|||||||
package visualiser.Controllers;
|
package visualiser.Controllers;
|
||||||
|
|
||||||
import javafx.fxml.Initializable;
|
import javafx.fxml.FXMLLoader;
|
||||||
|
import javafx.scene.Parent;
|
||||||
import java.net.URL;
|
import javafx.scene.Scene;
|
||||||
import java.util.ResourceBundle;
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.stage.Modality;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
import visualiser.app.App;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controller parent for app controllers.
|
* Abstract controller class to give each subclass the functionality to load
|
||||||
* Created by fwy13 on 15/03/2017.
|
* a new scene into the existing stage, or create a new popup window.
|
||||||
*/
|
*/
|
||||||
public abstract class Controller implements Initializable {
|
public abstract class Controller {
|
||||||
protected MainController parent;
|
private Stage stage = App.getStage();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the title screen again when app is already running.
|
||||||
|
* @throws IOException if a problem with the title.fxml
|
||||||
|
*/
|
||||||
|
protected void loadTitleScreen() throws IOException {
|
||||||
|
FXMLLoader loader = new FXMLLoader(getClass().getResource("/visualiser/scenes/title.fxml"));
|
||||||
|
Parent root = loader.load();
|
||||||
|
stage.setResizable(false);
|
||||||
|
Scene scene = new Scene(root);
|
||||||
|
addCssStyle(scene);
|
||||||
|
stage.setScene(scene);
|
||||||
|
stage.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to load a new scene in the currently open stage.
|
||||||
|
* @param fxmlUrl the URL of the FXML file to be loaded
|
||||||
|
* @return the controller of the new scene
|
||||||
|
* @throws IOException if there is an issue with the fxmlUrl
|
||||||
|
*/
|
||||||
|
protected Controller loadScene(String fxmlUrl) throws IOException {
|
||||||
|
// load the correct fxml file
|
||||||
|
FXMLLoader loader = new FXMLLoader();
|
||||||
|
loader.setLocation(getClass().getResource
|
||||||
|
("/visualiser/scenes/"+fxmlUrl));
|
||||||
|
Parent root = loader.load();
|
||||||
|
|
||||||
|
// reuse previous stage and it's window size
|
||||||
|
Stage stage = App.getStage();
|
||||||
|
Double stageHeight = stage.getHeight();
|
||||||
|
Double stageWidth = stage.getWidth();
|
||||||
|
|
||||||
|
// set new scene into existing window
|
||||||
|
Scene scene = new Scene(root, stageWidth, stageHeight);
|
||||||
|
addCssStyle(scene);
|
||||||
|
stage.setScene(scene);
|
||||||
|
stage.setResizable(true);
|
||||||
|
stage.show();
|
||||||
|
stage.setHeight(stageHeight);
|
||||||
|
stage.setWidth(stageWidth);
|
||||||
|
stage.sizeToScene();
|
||||||
|
|
||||||
|
// return controller for the loaded fxml scene
|
||||||
|
return loader.getController();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the parent of the application
|
* Used to load a scene in a new separate popup stage.
|
||||||
*
|
* @param fxmlUrl the URL of the FXML file to be loaded
|
||||||
* @param parent controller
|
* @param title title for the new window
|
||||||
|
* @param modality modality settings for popup window
|
||||||
|
* @return the controller of the new scene
|
||||||
|
* @throws IOException if there is an issue with the fxmlUrl
|
||||||
*/
|
*/
|
||||||
public void setParent(MainController parent) {
|
protected Controller loadPopupScene(String fxmlUrl, String title, Modality
|
||||||
this.parent = parent;
|
modality) throws IOException {
|
||||||
|
// load the correct fxml scene
|
||||||
|
FXMLLoader loader = new FXMLLoader();
|
||||||
|
loader.setLocation(getClass().getResource(
|
||||||
|
"/visualiser/scenes/" + fxmlUrl));
|
||||||
|
Parent root = loader.load();
|
||||||
|
|
||||||
|
// create a new 'pop-up' window
|
||||||
|
Stage stage = new Stage();
|
||||||
|
stage.initModality(modality);
|
||||||
|
stage.setTitle(title);
|
||||||
|
stage.centerOnScreen();
|
||||||
|
stage.getIcons().add(new Image(getClass().getClassLoader().getResourceAsStream("images/SailIcon.png")));
|
||||||
|
Scene scene = new Scene(root);
|
||||||
|
addCssStyle(scene);
|
||||||
|
stage.setScene(scene);
|
||||||
|
stage.show();
|
||||||
|
|
||||||
|
// return controller for the loaded fxml scene
|
||||||
|
return loader.getController();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialisation class that is run on start up.
|
* Adds the relevant CSS styling to the scene being loaded.
|
||||||
*
|
* @param scene new scene to be loaded and displayed
|
||||||
* @param location resources location
|
|
||||||
* @param resources resources bundle
|
|
||||||
*/
|
*/
|
||||||
@Override
|
private void addCssStyle(Scene scene){
|
||||||
public abstract void initialize(URL location, ResourceBundle resources);
|
if (App.dayMode) {
|
||||||
|
scene.getStylesheets().add("/css/dayMode.css");
|
||||||
|
} else {
|
||||||
|
scene.getStylesheets().add("/css/nightMode.css");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,92 +0,0 @@
|
|||||||
package visualiser.Controllers;
|
|
||||||
|
|
||||||
|
|
||||||
import javafx.collections.ObservableList;
|
|
||||||
import javafx.fxml.FXML;
|
|
||||||
import javafx.scene.control.Label;
|
|
||||||
import javafx.scene.control.TableColumn;
|
|
||||||
import javafx.scene.control.TableView;
|
|
||||||
import javafx.scene.layout.AnchorPane;
|
|
||||||
import visualiser.model.VisualiserBoat;
|
|
||||||
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.ResourceBundle;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finish Screen for when the race finishes.
|
|
||||||
*/
|
|
||||||
public class FinishController extends Controller {
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
AnchorPane finishWrapper;
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
TableView<VisualiserBoat> boatInfoTable;
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
TableColumn<VisualiserBoat, String> boatRankColumn;
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
TableColumn<VisualiserBoat, String> boatNameColumn;
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
Label raceWinnerLabel;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The boats to display on the table.
|
|
||||||
*/
|
|
||||||
private ObservableList<VisualiserBoat> boats;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ctor.
|
|
||||||
*/
|
|
||||||
public FinishController() {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initialize(URL location, ResourceBundle resources){
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets up the finish table
|
|
||||||
* @param boats Boats to display
|
|
||||||
*/
|
|
||||||
private void setFinishTable(ObservableList<VisualiserBoat> boats) {
|
|
||||||
this.boats = boats;
|
|
||||||
|
|
||||||
//Set contents.
|
|
||||||
boatInfoTable.setItems(boats);
|
|
||||||
|
|
||||||
//Name.
|
|
||||||
boatNameColumn.setCellValueFactory(cellData -> cellData.getValue().nameProperty());
|
|
||||||
|
|
||||||
//Rank/position.
|
|
||||||
boatRankColumn.setCellValueFactory(cellData -> cellData.getValue().placingProperty());
|
|
||||||
|
|
||||||
|
|
||||||
//Winner label.
|
|
||||||
if (boats.size() > 0) {
|
|
||||||
raceWinnerLabel.setText("Winner: " + boatNameColumn.getCellObservableValue(0).getValue());
|
|
||||||
raceWinnerLabel.setWrapText(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display the table
|
|
||||||
* @param boats boats to display on the table.
|
|
||||||
*/
|
|
||||||
public void enterFinish(ObservableList<VisualiserBoat> boats){
|
|
||||||
finishWrapper.setVisible(true);
|
|
||||||
setFinishTable(boats);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,148 +0,0 @@
|
|||||||
package visualiser.Controllers;
|
|
||||||
|
|
||||||
import javafx.fxml.FXML;
|
|
||||||
import javafx.scene.control.Button;
|
|
||||||
import javafx.scene.control.TextField;
|
|
||||||
import javafx.scene.image.Image;
|
|
||||||
import javafx.scene.image.ImageView;
|
|
||||||
import javafx.scene.layout.AnchorPane;
|
|
||||||
import javafx.scene.layout.GridPane;
|
|
||||||
import javafx.stage.Stage;
|
|
||||||
import javafx.scene.media.AudioClip;
|
|
||||||
import mock.app.Event;
|
|
||||||
import org.xml.sax.SAXException;
|
|
||||||
import mock.exceptions.EventConstructionException;
|
|
||||||
import shared.exceptions.InvalidBoatDataException;
|
|
||||||
import shared.exceptions.InvalidRaceDataException;
|
|
||||||
import shared.exceptions.InvalidRegattaDataException;
|
|
||||||
import shared.exceptions.XMLReaderException;
|
|
||||||
|
|
||||||
import javax.xml.bind.JAXBException;
|
|
||||||
import javax.xml.parsers.ParserConfigurationException;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.Socket;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.ResourceBundle;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Controller for Hosting a game.
|
|
||||||
*/
|
|
||||||
public class HostController extends Controller {
|
|
||||||
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
TextField gameNameField;
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
TextField hostNameField;
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
AnchorPane hostWrapper;
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
Button previousButton;
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
Button nextButton;
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
ImageView mapImage;
|
|
||||||
|
|
||||||
private Event game;
|
|
||||||
|
|
||||||
private ArrayList<Image> listOfMaps;
|
|
||||||
private int currentMapIndex = 0;
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initialize(URL location, ResourceBundle resources){
|
|
||||||
Image ac35Map = new Image(getClass().getClassLoader().getResourceAsStream("images/AC35_Racecourse_MAP.png"));
|
|
||||||
Image oMap = new Image(getClass().getClassLoader().getResourceAsStream("images/oMapLayout.png"));
|
|
||||||
Image iMap = new Image(getClass().getClassLoader().getResourceAsStream("images/iMapLayout.png"));
|
|
||||||
Image mMap = new Image(getClass().getClassLoader().getResourceAsStream("images/mMapLayout.png"));
|
|
||||||
|
|
||||||
listOfMaps = new ArrayList(Arrays.asList(ac35Map, oMap, iMap, mMap));
|
|
||||||
mapImage.setImage(listOfMaps.get(currentMapIndex));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hosts a game
|
|
||||||
* @throws IOException if socket cannot be connected to
|
|
||||||
*/
|
|
||||||
public void hostGamePressed() throws IOException{
|
|
||||||
try {
|
|
||||||
this.game = new Event(false, currentMapIndex);
|
|
||||||
connectSocket("localhost", 4942);
|
|
||||||
} catch (EventConstructionException e) {
|
|
||||||
Logger.getGlobal().log(Level.SEVERE, "Could not create Event.", e);
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void endEvent() throws IOException {
|
|
||||||
game.endEvent();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Connect to a socket
|
|
||||||
* @param address address of the server
|
|
||||||
* @param port port that the server is run off
|
|
||||||
*/
|
|
||||||
public void connectSocket(String address, int port) {
|
|
||||||
try{
|
|
||||||
Socket socket = new Socket(address, port);
|
|
||||||
hostWrapper.setVisible(false);
|
|
||||||
parent.enterLobby(socket, true);
|
|
||||||
} catch (IOException e) { /* Never reached */ }
|
|
||||||
}
|
|
||||||
|
|
||||||
public AnchorPane startWrapper(){
|
|
||||||
return hostWrapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hosts a game.
|
|
||||||
*/
|
|
||||||
public void hostGame(){
|
|
||||||
mapImage.fitWidthProperty().bind(((Stage) mapImage.getScene().getWindow()).widthProperty().multiply(0.6));
|
|
||||||
hostWrapper.setVisible(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Menu button pressed. Prompt alert then return to menu
|
|
||||||
*/
|
|
||||||
public void menuBtnPressed(){
|
|
||||||
AudioClip sound = new AudioClip(this.getClass().getResource("/visualiser/sounds/buttonpress.wav").toExternalForm());
|
|
||||||
sound.play();
|
|
||||||
hostWrapper.setVisible(false);
|
|
||||||
parent.enterTitle();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void nextImage(){
|
|
||||||
increaseIndex();
|
|
||||||
mapImage.setImage(listOfMaps.get(currentMapIndex));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void previousImage(){
|
|
||||||
decreaseIndex();
|
|
||||||
mapImage.setImage(listOfMaps.get(currentMapIndex));
|
|
||||||
}
|
|
||||||
private void increaseIndex(){
|
|
||||||
currentMapIndex = (currentMapIndex + 1)%listOfMaps.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void decreaseIndex(){
|
|
||||||
currentMapIndex = ((((currentMapIndex - 1)%listOfMaps.size())+listOfMaps.size())%listOfMaps.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setGameType(int gameType){
|
|
||||||
this.currentMapIndex = gameType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getGameType(){ return this.currentMapIndex; }
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -0,0 +1,143 @@
|
|||||||
|
package visualiser.Controllers;
|
||||||
|
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.control.Alert;
|
||||||
|
import javafx.scene.control.ButtonType;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
|
import mock.app.Event;
|
||||||
|
import mock.exceptions.EventConstructionException;
|
||||||
|
import visualiser.app.App;
|
||||||
|
import visualiser.app.MatchBrowserSingleton;
|
||||||
|
import visualiser.network.MatchBrowserInterface;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.DatagramSocket;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for Hosting a game.
|
||||||
|
*/
|
||||||
|
public class HostGameController extends Controller {
|
||||||
|
private @FXML ImageView mapImage;
|
||||||
|
private ArrayList<Image> listOfMaps;
|
||||||
|
private int currentMapIndex = 0;
|
||||||
|
private DatagramSocket udpSocket;
|
||||||
|
private MatchBrowserInterface matchBrowserInterface;
|
||||||
|
|
||||||
|
public void initialize() {
|
||||||
|
loadMaps();
|
||||||
|
this.udpSocket = MatchBrowserSingleton.getInstance().getUdpSocket();
|
||||||
|
this.matchBrowserInterface = MatchBrowserSingleton.getInstance().getMatchBrowserInterface();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads in the list of playable maps to be selected from.
|
||||||
|
*/
|
||||||
|
private void loadMaps(){
|
||||||
|
// image preview of maps
|
||||||
|
Image ac35Map = new Image(getClass().getClassLoader().getResourceAsStream("images/AC35_Racecourse_MAP.png"));
|
||||||
|
Image oMap = new Image(getClass().getClassLoader().getResourceAsStream("images/oMapLayout.png"));
|
||||||
|
Image iMap = new Image(getClass().getClassLoader().getResourceAsStream("images/iMapLayout.png"));
|
||||||
|
Image mMap = new Image(getClass().getClassLoader().getResourceAsStream("images/mMapLayout.png"));
|
||||||
|
|
||||||
|
listOfMaps = new ArrayList(Arrays.asList(ac35Map, oMap, iMap, mMap));
|
||||||
|
mapImage.setImage(listOfMaps.get(currentMapIndex));
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
mapImage.fitWidthProperty()
|
||||||
|
.bind(mapImage.getScene().getWindow().widthProperty().multiply(0.6));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hosts a game
|
||||||
|
*/
|
||||||
|
public void hostGamePressed() {
|
||||||
|
try {
|
||||||
|
App.game = new Event(false, currentMapIndex);
|
||||||
|
App.gameType = currentMapIndex;
|
||||||
|
connectSocket("localhost", 4942);
|
||||||
|
alertMatchBrowser();
|
||||||
|
|
||||||
|
} catch (EventConstructionException e) {
|
||||||
|
Logger.getGlobal().log(Level.SEVERE, "Could not create Event.", e);
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends info to the match browser so clients can see it
|
||||||
|
*/
|
||||||
|
public void alertMatchBrowser(){
|
||||||
|
try{
|
||||||
|
matchBrowserInterface.startSendingHostData(App.game.getHostedGameData(), udpSocket);
|
||||||
|
}catch (IOException e){
|
||||||
|
System.err.println("failed to send out hosted game info");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connect to a socket
|
||||||
|
* @param address address of the server
|
||||||
|
* @param port port that the server is run off
|
||||||
|
* @throws IOException socket error
|
||||||
|
*/
|
||||||
|
public void connectSocket(String address, int port) throws IOException {
|
||||||
|
Socket socket = new Socket(address, port);
|
||||||
|
InGameLobbyController iglc = (InGameLobbyController)loadScene("gameLobby.fxml");
|
||||||
|
iglc.enterGameLobby(socket, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Menu button pressed. Prompt alert then return to menu
|
||||||
|
* @throws IOException socket error
|
||||||
|
*/
|
||||||
|
public void menuBtnPressed() throws Exception {
|
||||||
|
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
|
||||||
|
alert.setTitle("Quitting race");
|
||||||
|
alert.setContentText("Do you wish to quit the race?");
|
||||||
|
alert.setHeaderText("You are about to quit the race");
|
||||||
|
Optional<ButtonType> result = alert.showAndWait();
|
||||||
|
if(result.get() == ButtonType.OK){
|
||||||
|
loadTitleScreen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method called when the 'next' arrow button is pressed. It is used to
|
||||||
|
* change the currently displayed map preview to the next in the list.
|
||||||
|
*/
|
||||||
|
public void nextImage(){
|
||||||
|
// increase index
|
||||||
|
currentMapIndex = (currentMapIndex + 1) % listOfMaps.size();
|
||||||
|
// update map preview
|
||||||
|
mapImage.setImage(listOfMaps.get(currentMapIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method called when the 'previous' arrow button is pressed. It is used to
|
||||||
|
* change the currently displayed map preview to the previous in the list.
|
||||||
|
*/
|
||||||
|
public void previousImage(){
|
||||||
|
// decrease index
|
||||||
|
currentMapIndex = ((((currentMapIndex - 1) % listOfMaps.size()) +
|
||||||
|
listOfMaps.size()) % listOfMaps.size());
|
||||||
|
// update map preview
|
||||||
|
mapImage.setImage(listOfMaps.get(currentMapIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCurrentMapIndex(Integer index){
|
||||||
|
this.currentMapIndex = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,365 @@
|
|||||||
|
package visualiser.Controllers;
|
||||||
|
|
||||||
|
import com.interactivemesh.jfx.importer.stl.StlMeshImporter;
|
||||||
|
import javafx.animation.AnimationTimer;
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.collections.ListChangeListener;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.control.Alert;
|
||||||
|
import javafx.scene.control.ButtonType;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
|
import javafx.scene.layout.AnchorPane;
|
||||||
|
import javafx.scene.layout.GridPane;
|
||||||
|
import javafx.scene.shape.MeshView;
|
||||||
|
import mock.app.Event;
|
||||||
|
import network.Messages.Enums.RaceStatusEnum;
|
||||||
|
import network.Messages.Enums.RequestToJoinEnum;
|
||||||
|
import visualiser.app.App;
|
||||||
|
import visualiser.gameController.ControllerClient;
|
||||||
|
import visualiser.layout.SeaSurface;
|
||||||
|
import visualiser.layout.Subject3D;
|
||||||
|
import visualiser.layout.View3D;
|
||||||
|
import visualiser.model.VisualiserBoat;
|
||||||
|
import visualiser.model.VisualiserRaceEvent;
|
||||||
|
import visualiser.model.VisualiserRaceState;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for Hosting a game.
|
||||||
|
*/
|
||||||
|
public class InGameLobbyController extends Controller {
|
||||||
|
@FXML
|
||||||
|
private ImageView imageView;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
GridPane playerContainer;
|
||||||
|
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label playerLabel;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label playerLabel2;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label playerLabel3;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label playerLabel4;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label playerLabel5;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label playerLabel6;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label countdownLabel;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private AnchorPane countdownTenPane;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label countdownTenText;
|
||||||
|
|
||||||
|
private Event game;
|
||||||
|
|
||||||
|
private View3D playerBoat;
|
||||||
|
|
||||||
|
private VisualiserRaceEvent visualiserRaceEvent;
|
||||||
|
|
||||||
|
private boolean isHost;
|
||||||
|
|
||||||
|
private ControllerClient controllerClient;
|
||||||
|
|
||||||
|
private ArrayList<Label> allPlayerLabels;
|
||||||
|
|
||||||
|
private ObservableList<Subject3D> subjects = FXCollections.observableArrayList();
|
||||||
|
|
||||||
|
private StlMeshImporter importer;
|
||||||
|
|
||||||
|
private PopulatePlayers lobbyUpdateListener;
|
||||||
|
|
||||||
|
|
||||||
|
public void initialize() {
|
||||||
|
allPlayerLabels = new ArrayList(Arrays.asList(playerLabel, playerLabel2,
|
||||||
|
playerLabel3,
|
||||||
|
playerLabel4,
|
||||||
|
playerLabel5,
|
||||||
|
playerLabel6));
|
||||||
|
|
||||||
|
URL asset = HostGameController.class.getClassLoader().getResource("assets/V1.2 Complete Boat.stl");
|
||||||
|
importer = new StlMeshImporter();
|
||||||
|
importer.read(asset);
|
||||||
|
lobbyUpdateListener = new PopulatePlayers();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resetLobby(){
|
||||||
|
for (Label label: allPlayerLabels){
|
||||||
|
label.setText("No Player");
|
||||||
|
}
|
||||||
|
List<Node> nodeCopy = new ArrayList(playerContainer.getChildren());
|
||||||
|
for (Node node: nodeCopy){
|
||||||
|
if (node instanceof View3D){
|
||||||
|
playerContainer.getChildren().remove(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PopulatePlayers implements ListChangeListener {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChanged(Change change) {
|
||||||
|
Platform.runLater(
|
||||||
|
() -> {
|
||||||
|
while (change.next()){
|
||||||
|
if (change.wasAdded() || change.wasRemoved() || change.wasUpdated()){
|
||||||
|
try{
|
||||||
|
resetLobby();
|
||||||
|
int count = 0;
|
||||||
|
int row = 0;
|
||||||
|
for (VisualiserBoat boat :visualiserRaceEvent.getVisualiserRaceState().getBoats()) {
|
||||||
|
View3D playerBoatToSet = new View3D();
|
||||||
|
playerBoatToSet.setItems(subjects);
|
||||||
|
|
||||||
|
playerContainer.add(playerBoatToSet, (count % 3) , row);
|
||||||
|
playerContainer.setMargin(playerBoatToSet, new Insets(10, 10, 10, 10));
|
||||||
|
|
||||||
|
SeaSurface sea = new SeaSurface(750, 200);
|
||||||
|
sea.setX(250);
|
||||||
|
sea.setZ(210);
|
||||||
|
subjects.add(sea);
|
||||||
|
|
||||||
|
MeshView mesh = new MeshView(importer.getImport());
|
||||||
|
Subject3D subject = new Subject3D(mesh,0);
|
||||||
|
subjects.add(subject);
|
||||||
|
|
||||||
|
playerBoatToSet.setDistance(50);
|
||||||
|
playerBoatToSet.setYaw(45);
|
||||||
|
playerBoatToSet.setPitch(20);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
AnimationTimer rotate = new AnimationTimer() {
|
||||||
|
@Override
|
||||||
|
public void handle(long now) {
|
||||||
|
subject.setHeading(subject.getHeading().getAngle() + 0.1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
rotate.start();
|
||||||
|
|
||||||
|
allPlayerLabels.get(count).setText("Player: " + boat.getSourceID());
|
||||||
|
allPlayerLabels.get(count).toFront();
|
||||||
|
count += 1;
|
||||||
|
if (count > 2){
|
||||||
|
row = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(ConcurrentModificationException e){
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
private void populatePlayers(ListChangeListener.Change change){
|
||||||
|
}*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the race.
|
||||||
|
*/
|
||||||
|
private void startRace() {
|
||||||
|
//Initialises the race clock.
|
||||||
|
initialiseRaceClock(this.visualiserRaceEvent.getVisualiserRaceState());
|
||||||
|
|
||||||
|
|
||||||
|
//Starts the race countdown timer.
|
||||||
|
countdownTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialises the race clock/timer labels for the start time, current time, and remaining time.
|
||||||
|
* @param visualiserRace The race to get data from.
|
||||||
|
*/
|
||||||
|
private void initialiseRaceClock(VisualiserRaceState visualiserRace) {
|
||||||
|
//Remaining time.
|
||||||
|
initialiseRaceClockDuration(visualiserRace);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialises the race duration label.
|
||||||
|
* @param visualiserRace The race to get data from.
|
||||||
|
*/
|
||||||
|
private void initialiseRaceClockDuration(VisualiserRaceState visualiserRace) {
|
||||||
|
|
||||||
|
visualiserRace.getRaceClock().durationProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
countdownLabel.setText(newValue);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Countdown timer until race starts.
|
||||||
|
*/
|
||||||
|
private void countdownTimer() {
|
||||||
|
new AnimationTimer() {
|
||||||
|
@Override
|
||||||
|
public void handle(long arg0) {
|
||||||
|
|
||||||
|
//Get the current race status.
|
||||||
|
RaceStatusEnum raceStatus = visualiserRaceEvent.getVisualiserRaceState().getRaceStatusEnum();
|
||||||
|
|
||||||
|
//Try catch for getting interval times
|
||||||
|
try {
|
||||||
|
long interval = ChronoUnit.MILLIS.between(visualiserRaceEvent.getVisualiserRaceState().getRaceClock().getCurrentTime(), visualiserRaceEvent.getVisualiserRaceState().getRaceClock().getStartingTime());
|
||||||
|
if(interval<=10000){
|
||||||
|
countdownLabel.setVisible(false);
|
||||||
|
countdownTenPane.setVisible(true);
|
||||||
|
countdownTenText.setVisible(true);
|
||||||
|
}
|
||||||
|
countdownText(interval);
|
||||||
|
} catch (Exception e){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//If the race has reached the preparatory phase, or has started...
|
||||||
|
if (raceStatus == RaceStatusEnum.PREPARATORY || raceStatus == RaceStatusEnum.STARTED) {
|
||||||
|
//Stop this timer.
|
||||||
|
stop();
|
||||||
|
|
||||||
|
//Hide this, and display the race controller.
|
||||||
|
try {
|
||||||
|
visualiserRaceEvent.getVisualiserRaceState().getBoats().removeListener(lobbyUpdateListener);
|
||||||
|
|
||||||
|
RaceViewController rvc = (RaceViewController)
|
||||||
|
loadScene("raceView.fxml");
|
||||||
|
rvc.startRace(visualiserRaceEvent, controllerClient,
|
||||||
|
isHost);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void enterGameLobby(Socket socket, boolean isHost){
|
||||||
|
try {
|
||||||
|
|
||||||
|
this.visualiserRaceEvent = new VisualiserRaceEvent(socket, RequestToJoinEnum.PARTICIPANT);
|
||||||
|
this.isHost = isHost;
|
||||||
|
this.controllerClient = visualiserRaceEvent.getControllerClient();
|
||||||
|
|
||||||
|
this.visualiserRaceEvent.getVisualiserRaceState().getBoats().addListener(this.lobbyUpdateListener);
|
||||||
|
|
||||||
|
startRace();
|
||||||
|
} catch (IOException e) {
|
||||||
|
//TODO should probably let this propagate, so that we only enter this scene if everything works
|
||||||
|
Logger.getGlobal().log(Level.WARNING, "Could not connect to server.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Menu button pressed. Prompt alert then return to menu
|
||||||
|
* @throws IOException socket erro
|
||||||
|
*/
|
||||||
|
public void menuBtnPressed() throws IOException {
|
||||||
|
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
|
||||||
|
alert.setTitle("Quitting race");
|
||||||
|
alert.setContentText("Do you wish to quit the race?");
|
||||||
|
alert.setHeaderText("You are about to quit the race");
|
||||||
|
Optional<ButtonType> result = alert.showAndWait();
|
||||||
|
if(result.get() == ButtonType.OK){
|
||||||
|
visualiserRaceEvent.terminate();
|
||||||
|
|
||||||
|
try{
|
||||||
|
if(isHost) {
|
||||||
|
App.game.endEvent();
|
||||||
|
}
|
||||||
|
loadScene("title.fxml");
|
||||||
|
|
||||||
|
}catch (IOException ignore){};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start button pressed. Currently only prints out start
|
||||||
|
*/
|
||||||
|
public void startBtnPressed(){
|
||||||
|
//System.out.println("Should start the race. This button is only visible for the host");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void joinSpecPressed(){
|
||||||
|
//System.out.println("Spectator list pressed. Joining spectators");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void joinRacePressed(){
|
||||||
|
//System.out.println("Empty race user pane pressed. Joining racers");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Countdown interval checker that updates the countdown text
|
||||||
|
* @param interval Countdown interval to check
|
||||||
|
*/
|
||||||
|
private void countdownText(long interval){
|
||||||
|
|
||||||
|
//Do nothing if 5 seconds or less to go
|
||||||
|
if (interval <=5000){
|
||||||
|
countdownTenPane.setVisible(false);
|
||||||
|
//countdownTenText.setText("5");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//6 seconds left. Display 1 second for countdown
|
||||||
|
if (interval <=6000){
|
||||||
|
countdownTenText.setText("1");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//7 seconds left. Display 2 seconds for countdown
|
||||||
|
if (interval <=7000){
|
||||||
|
countdownTenText.setText("2");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//8 seconds left. Display 3 seconds for countdown
|
||||||
|
if (interval <=8000){
|
||||||
|
countdownTenText.setText("3");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//9 seconds left. Display 4 seconds for countdown
|
||||||
|
if (interval <=9000){
|
||||||
|
countdownTenText.setText("4");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//10 seconds left. Display 5 seconds for countdown
|
||||||
|
if (interval <=10000){
|
||||||
|
countdownTenText.setText("5");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,158 +0,0 @@
|
|||||||
package visualiser.Controllers;
|
|
||||||
|
|
||||||
import javafx.collections.ObservableList;
|
|
||||||
import javafx.fxml.FXML;
|
|
||||||
import javafx.scene.layout.AnchorPane;
|
|
||||||
import javafx.scene.media.AudioClip;
|
|
||||||
import visualiser.app.App;
|
|
||||||
import visualiser.gameController.ControllerClient;
|
|
||||||
import visualiser.model.VisualiserBoat;
|
|
||||||
import visualiser.model.VisualiserRaceEvent;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.Socket;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.ResourceBundle;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Controller that everything is overlayed onto. This makes it so that changing scenes does not resize your stage.
|
|
||||||
*/
|
|
||||||
public class MainController extends Controller {
|
|
||||||
|
|
||||||
@FXML private StartController startController;
|
|
||||||
@FXML private RaceController raceController;
|
|
||||||
@FXML private ConnectionController connectionController;
|
|
||||||
@FXML private FinishController finishController;
|
|
||||||
@FXML private TitleController titleController;
|
|
||||||
@FXML private HostController hostController;
|
|
||||||
@FXML private LobbyController lobbyController;
|
|
||||||
|
|
||||||
private AudioClip sound;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ctor.
|
|
||||||
*/
|
|
||||||
public MainController() {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transitions from the StartController screen (displays pre-race information) to the RaceController (displays the actual race).
|
|
||||||
* @param visualiserRace The object modelling the race.
|
|
||||||
* @param controllerClient Socket Client that manipulates the controller.
|
|
||||||
* @param isHost if the client is the host of a race or not.
|
|
||||||
*/
|
|
||||||
public void beginRace(VisualiserRaceEvent visualiserRace, ControllerClient controllerClient, Boolean isHost) {
|
|
||||||
raceController.startRace(visualiserRace, controllerClient, isHost);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void endEvent() throws IOException { hostController.endEvent(); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transitions from the server selection screen to the pre-race lobby for a given server.
|
|
||||||
* @param socket The server to read data from.
|
|
||||||
* @param isHost is connection a host
|
|
||||||
*/
|
|
||||||
public void enterLobby(Socket socket, Boolean isHost) {
|
|
||||||
sound = new AudioClip(this.getClass().getResource("/visualiser/sounds/buttonpress.wav").toExternalForm());
|
|
||||||
sound.play();
|
|
||||||
startController.enterLobby(socket, isHost);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transitions from the {@link RaceController} screen to the {@link FinishController} screen.
|
|
||||||
* @param boats The boats to display on the finish screen.
|
|
||||||
*/
|
|
||||||
public void enterFinish(ObservableList<VisualiserBoat> boats) {
|
|
||||||
finishController.enterFinish(boats);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transitions into the title screen
|
|
||||||
*/
|
|
||||||
public void enterTitle() {
|
|
||||||
titleController.enterTitle();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transitions into lobby screen
|
|
||||||
*/
|
|
||||||
public void enterLobby(){
|
|
||||||
sound = new AudioClip(this.getClass().getResource("/visualiser/sounds/buttonpress.wav").toExternalForm());
|
|
||||||
sound.play();
|
|
||||||
lobbyController.enterLobby(); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transitions into host game screen
|
|
||||||
*/
|
|
||||||
public void hostGame(){
|
|
||||||
sound = new AudioClip(this.getClass().getResource("/visualiser/sounds/buttonpress.wav").toExternalForm());
|
|
||||||
sound.play();
|
|
||||||
hostController.hostGame(); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets up the css for the start of the program
|
|
||||||
*/
|
|
||||||
public void startCss(){titleController.setDayMode();}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* host controller host a game
|
|
||||||
* @throws IOException throws exception
|
|
||||||
*/
|
|
||||||
public void beginGame() throws IOException {
|
|
||||||
hostController.hostGamePressed();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setGameType(int gameType){
|
|
||||||
hostController.setGameType(gameType);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getGameType(){ return hostController.getGameType(); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Main Controller for the applications will house the menu and the displayed pane.
|
|
||||||
*
|
|
||||||
* @param location of resources
|
|
||||||
* @param resources bundle
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void initialize(URL location, ResourceBundle resources) {
|
|
||||||
|
|
||||||
startController.setParent(this);
|
|
||||||
raceController.setParent(this);
|
|
||||||
connectionController.setParent(this);
|
|
||||||
finishController.setParent(this);
|
|
||||||
titleController.setParent(this);
|
|
||||||
hostController.setParent(this);
|
|
||||||
lobbyController.setParent(this);
|
|
||||||
|
|
||||||
|
|
||||||
AnchorPane.setTopAnchor(startController.startWrapper(), 0.0);
|
|
||||||
AnchorPane.setBottomAnchor(startController.startWrapper(), 0.0);
|
|
||||||
AnchorPane.setLeftAnchor(startController.startWrapper(), 0.0);
|
|
||||||
AnchorPane.setRightAnchor(startController.startWrapper(), 0.0);
|
|
||||||
|
|
||||||
AnchorPane.setTopAnchor(lobbyController.startWrapper(), 0.0);
|
|
||||||
AnchorPane.setBottomAnchor(lobbyController.startWrapper(), 0.0);
|
|
||||||
AnchorPane.setLeftAnchor(lobbyController.startWrapper(), 0.0);
|
|
||||||
AnchorPane.setRightAnchor(lobbyController.startWrapper(), 0.0);
|
|
||||||
|
|
||||||
AnchorPane.setTopAnchor(hostController.startWrapper(), 0.0);
|
|
||||||
AnchorPane.setBottomAnchor(hostController.startWrapper(), 0.0);
|
|
||||||
AnchorPane.setLeftAnchor(hostController.startWrapper(), 0.0);
|
|
||||||
AnchorPane.setRightAnchor(hostController.startWrapper(), 0.0);
|
|
||||||
|
|
||||||
AnchorPane.setTopAnchor(finishController.finishWrapper, 0.0);
|
|
||||||
AnchorPane.setBottomAnchor(finishController.finishWrapper, 0.0);
|
|
||||||
AnchorPane.setLeftAnchor(finishController.finishWrapper, 0.0);
|
|
||||||
AnchorPane.setRightAnchor(finishController.finishWrapper, 0.0);
|
|
||||||
|
|
||||||
AnchorPane.setTopAnchor(titleController.titleWrapper, 0.0);
|
|
||||||
AnchorPane.setBottomAnchor(titleController.titleWrapper, 0.0);
|
|
||||||
AnchorPane.setLeftAnchor(titleController.titleWrapper, 0.0);
|
|
||||||
AnchorPane.setRightAnchor(titleController.titleWrapper, 0.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
package visualiser.Controllers;
|
||||||
|
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.TableColumn;
|
||||||
|
import javafx.scene.control.TableView;
|
||||||
|
import visualiser.model.VisualiserBoat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finish Screen for when the race finishes.
|
||||||
|
*/
|
||||||
|
public class RaceFinishController extends Controller {
|
||||||
|
private @FXML TableView<VisualiserBoat> boatInfoTable;
|
||||||
|
private @FXML TableColumn<VisualiserBoat, String> boatRankColumn;
|
||||||
|
private @FXML TableColumn<VisualiserBoat, String> boatNameColumn;
|
||||||
|
private @FXML Label raceWinnerLabel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the table
|
||||||
|
* @param boats boats to display on the table.
|
||||||
|
*/
|
||||||
|
public void loadFinish(ObservableList<VisualiserBoat> boats) {
|
||||||
|
// set table contents
|
||||||
|
boatInfoTable.setItems(boats);
|
||||||
|
//Name.
|
||||||
|
boatNameColumn.setCellValueFactory(cellData -> cellData.getValue().nameProperty());
|
||||||
|
//Rank/position.
|
||||||
|
boatRankColumn.setCellValueFactory(cellData -> cellData.getValue().placingProperty());
|
||||||
|
|
||||||
|
//Winner label.
|
||||||
|
if (boats.size() > 0) {
|
||||||
|
raceWinnerLabel.setText("Winner: " +
|
||||||
|
boatNameColumn.getCellObservableValue(0).getValue());
|
||||||
|
raceWinnerLabel.setWrapText(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,141 @@
|
|||||||
|
package visualiser.Controllers;
|
||||||
|
|
||||||
|
import javafx.animation.AnimationTimer;
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.TableColumn;
|
||||||
|
import javafx.scene.control.TableView;
|
||||||
|
import network.Messages.Enums.RaceStatusEnum;
|
||||||
|
import network.Messages.Enums.RequestToJoinEnum;
|
||||||
|
import visualiser.gameController.ControllerClient;
|
||||||
|
import visualiser.model.VisualiserBoat;
|
||||||
|
import visualiser.model.VisualiserRaceEvent;
|
||||||
|
import visualiser.model.VisualiserRaceState;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller to for waiting for the race to start.
|
||||||
|
*/
|
||||||
|
public class RaceStartController extends Controller {
|
||||||
|
private @FXML Label raceTitleLabel;
|
||||||
|
private @FXML Label raceStartLabel;
|
||||||
|
private @FXML Label timeZoneTime;
|
||||||
|
private @FXML Label timer;
|
||||||
|
private @FXML TableView<VisualiserBoat> boatNameTable;
|
||||||
|
private @FXML TableColumn<VisualiserBoat, String> boatNameColumn;
|
||||||
|
private @FXML TableColumn<VisualiserBoat, String> boatCodeColumn;
|
||||||
|
private @FXML Label raceStatusLabel;
|
||||||
|
|
||||||
|
private VisualiserRaceEvent visualiserRaceEvent;
|
||||||
|
private VisualiserRaceState raceState;
|
||||||
|
private ControllerClient controllerClient;
|
||||||
|
private boolean isHost;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show starting information for a race given a socket.
|
||||||
|
* Intended to be called on loading the scene.
|
||||||
|
* @param socket network source of information
|
||||||
|
* @param isHost is user a host
|
||||||
|
*/
|
||||||
|
public void enterLobby(Socket socket, Boolean isHost) {
|
||||||
|
try {
|
||||||
|
this.isHost = isHost;
|
||||||
|
this.visualiserRaceEvent = new VisualiserRaceEvent(socket, RequestToJoinEnum.PARTICIPANT);
|
||||||
|
this.controllerClient = visualiserRaceEvent.getControllerClient();
|
||||||
|
this.raceState = visualiserRaceEvent.getVisualiserRaceState();
|
||||||
|
showRaceDetails();
|
||||||
|
} catch (IOException e) {
|
||||||
|
//TODO should probably let this propagate, so that we only enter this scene if everything works
|
||||||
|
Logger.getGlobal()
|
||||||
|
.log(Level.WARNING, "Could not connect to server.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays details and starts the timer for the race being started
|
||||||
|
*/
|
||||||
|
private void showRaceDetails() {
|
||||||
|
raceTitleLabel.setText(this.raceState.getRegattaName());
|
||||||
|
initialiseBoatTable();
|
||||||
|
initialiseRaceClock();
|
||||||
|
countdownTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialises the boat table that is to be shown on the pane.
|
||||||
|
*/
|
||||||
|
private void initialiseBoatTable() {
|
||||||
|
//Get the boats.
|
||||||
|
ObservableList<VisualiserBoat> boats =
|
||||||
|
this.raceState.getBoats();
|
||||||
|
|
||||||
|
//Populate table.
|
||||||
|
boatNameTable.setItems(boats);
|
||||||
|
boatNameColumn.setCellValueFactory(cellData -> cellData.getValue().nameProperty());
|
||||||
|
boatCodeColumn.setCellValueFactory(cellData -> cellData.getValue().countryProperty());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialises the race clock/timer labels for the start time, current time, and remaining time.
|
||||||
|
*/
|
||||||
|
private void initialiseRaceClock() {
|
||||||
|
raceStartLabel.setText(
|
||||||
|
this.raceState.getRaceClock().getStartingTimeString());
|
||||||
|
|
||||||
|
// init clock start time
|
||||||
|
this.raceState.getRaceClock().startingTimeProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
raceStartLabel.setText(newValue);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// init clock current time
|
||||||
|
this.raceState.getRaceClock().currentTimeProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
timeZoneTime.setText(newValue);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// init clock remaining time
|
||||||
|
this.raceState.getRaceClock().durationProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
timer.setText(newValue);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Countdown timer until race starts.
|
||||||
|
*/
|
||||||
|
private void countdownTimer() {
|
||||||
|
new AnimationTimer() {
|
||||||
|
@Override
|
||||||
|
public void handle(long arg0) {
|
||||||
|
// display current race status
|
||||||
|
RaceStatusEnum raceStatus = raceState.getRaceStatusEnum();
|
||||||
|
raceStatusLabel.setText("Race Status: " + raceStatus.name());
|
||||||
|
|
||||||
|
// if race is in PREPARATORY or STARTED status
|
||||||
|
if (raceStatus == RaceStatusEnum.PREPARATORY || raceStatus == RaceStatusEnum.STARTED) {
|
||||||
|
stop(); // stop this timer
|
||||||
|
// load up the race scene
|
||||||
|
try {
|
||||||
|
RaceViewController rvc = (RaceViewController)
|
||||||
|
loadScene("raceView.fxml");
|
||||||
|
rvc.startRace(visualiserRaceEvent, controllerClient,
|
||||||
|
isHost);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,271 +0,0 @@
|
|||||||
package visualiser.Controllers;
|
|
||||||
|
|
||||||
import javafx.animation.AnimationTimer;
|
|
||||||
import javafx.application.Platform;
|
|
||||||
import javafx.collections.ObservableList;
|
|
||||||
import javafx.fxml.FXML;
|
|
||||||
import javafx.scene.control.Label;
|
|
||||||
import javafx.scene.control.TableColumn;
|
|
||||||
import javafx.scene.control.TableView;
|
|
||||||
import javafx.scene.layout.AnchorPane;
|
|
||||||
import javafx.scene.layout.GridPane;
|
|
||||||
import mock.model.commandFactory.CompositeCommand;
|
|
||||||
import network.Messages.Enums.RaceStatusEnum;
|
|
||||||
import network.Messages.Enums.RequestToJoinEnum;
|
|
||||||
import network.Messages.LatestMessages;
|
|
||||||
import shared.dataInput.*;
|
|
||||||
import shared.enums.XMLFileType;
|
|
||||||
import shared.exceptions.InvalidBoatDataException;
|
|
||||||
import shared.exceptions.InvalidRaceDataException;
|
|
||||||
import shared.exceptions.InvalidRegattaDataException;
|
|
||||||
import shared.exceptions.XMLReaderException;
|
|
||||||
import visualiser.gameController.ControllerClient;
|
|
||||||
import visualiser.model.VisualiserRaceState;
|
|
||||||
import visualiser.network.ServerConnection;
|
|
||||||
import visualiser.model.VisualiserBoat;
|
|
||||||
import visualiser.model.VisualiserRaceEvent;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.Socket;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Controller to for waiting for the race to start.
|
|
||||||
*/
|
|
||||||
public class StartController extends Controller {
|
|
||||||
|
|
||||||
@FXML private GridPane start;
|
|
||||||
@FXML private AnchorPane startWrapper;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The name of the race/regatta.
|
|
||||||
*/
|
|
||||||
@FXML private Label raceTitleLabel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The time the race starts at.
|
|
||||||
*/
|
|
||||||
@FXML private Label raceStartLabel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The current time at the race location.
|
|
||||||
*/
|
|
||||||
@FXML private Label timeZoneTime;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Time until the race starts.
|
|
||||||
*/
|
|
||||||
@FXML private Label timer;
|
|
||||||
|
|
||||||
@FXML private TableView<VisualiserBoat> boatNameTable;
|
|
||||||
@FXML private TableColumn<VisualiserBoat, String> boatNameColumn;
|
|
||||||
@FXML private TableColumn<VisualiserBoat, String> boatCodeColumn;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The status of the race.
|
|
||||||
*/
|
|
||||||
@FXML private Label raceStatusLabel;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The race + connection to server.
|
|
||||||
*/
|
|
||||||
private VisualiserRaceEvent visualiserRaceEvent;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes BoatActions to outgoing message queue.
|
|
||||||
*/
|
|
||||||
private ControllerClient controllerClient;
|
|
||||||
|
|
||||||
private boolean isHost;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ctor.
|
|
||||||
*/
|
|
||||||
public StartController() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initialize(URL location, ResourceBundle resources) {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts the race.
|
|
||||||
*/
|
|
||||||
private void startRace() {
|
|
||||||
|
|
||||||
//Initialise the boat table.
|
|
||||||
initialiseBoatTable(this.visualiserRaceEvent.getVisualiserRaceState());
|
|
||||||
|
|
||||||
//Initialise the race name.
|
|
||||||
initialiseRaceName(this.visualiserRaceEvent.getVisualiserRaceState());
|
|
||||||
|
|
||||||
//Initialises the race clock.
|
|
||||||
initialiseRaceClock(this.visualiserRaceEvent.getVisualiserRaceState());
|
|
||||||
|
|
||||||
//Starts the race countdown timer.
|
|
||||||
countdownTimer();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public AnchorPane startWrapper(){
|
|
||||||
return startWrapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialises the boat table that is to be shown on the pane.
|
|
||||||
* @param visualiserRace The race to get data from.
|
|
||||||
*/
|
|
||||||
private void initialiseBoatTable(VisualiserRaceState visualiserRace) {
|
|
||||||
|
|
||||||
//Get the boats.
|
|
||||||
ObservableList<VisualiserBoat> boats = visualiserRace.getBoats();
|
|
||||||
|
|
||||||
//Populate table.
|
|
||||||
boatNameTable.setItems(boats);
|
|
||||||
boatNameColumn.setCellValueFactory(cellData -> cellData.getValue().nameProperty());
|
|
||||||
boatCodeColumn.setCellValueFactory(cellData -> cellData.getValue().countryProperty());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialises the race name which is shown on the pane.
|
|
||||||
* @param visualiserRace The race to get data from.
|
|
||||||
*/
|
|
||||||
private void initialiseRaceName(VisualiserRaceState visualiserRace) {
|
|
||||||
|
|
||||||
raceTitleLabel.setText(visualiserRace.getRegattaName());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialises the race clock/timer labels for the start time, current time, and remaining time.
|
|
||||||
* @param visualiserRace The race to get data from.
|
|
||||||
*/
|
|
||||||
private void initialiseRaceClock(VisualiserRaceState visualiserRace) {
|
|
||||||
|
|
||||||
//Start time.
|
|
||||||
initialiseRaceClockStartTime(visualiserRace);
|
|
||||||
|
|
||||||
//Current time.
|
|
||||||
initialiseRaceClockCurrentTime(visualiserRace);
|
|
||||||
|
|
||||||
//Remaining time.
|
|
||||||
initialiseRaceClockDuration(visualiserRace);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialises the race current time label.
|
|
||||||
* @param visualiserRace The race to get data from.
|
|
||||||
*/
|
|
||||||
private void initialiseRaceClockStartTime(VisualiserRaceState visualiserRace) {
|
|
||||||
|
|
||||||
raceStartLabel.setText(visualiserRace.getRaceClock().getStartingTimeString());
|
|
||||||
|
|
||||||
visualiserRace.getRaceClock().startingTimeProperty().addListener((observable, oldValue, newValue) -> {
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
raceStartLabel.setText(newValue);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialises the race current time label.
|
|
||||||
* @param visualiserRace The race to get data from.
|
|
||||||
*/
|
|
||||||
private void initialiseRaceClockCurrentTime(VisualiserRaceState visualiserRace) {
|
|
||||||
|
|
||||||
visualiserRace.getRaceClock().currentTimeProperty().addListener((observable, oldValue, newValue) -> {
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
timeZoneTime.setText(newValue);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialises the race duration label.
|
|
||||||
* @param visualiserRace The race to get data from.
|
|
||||||
*/
|
|
||||||
private void initialiseRaceClockDuration(VisualiserRaceState visualiserRace) {
|
|
||||||
|
|
||||||
visualiserRace.getRaceClock().durationProperty().addListener((observable, oldValue, newValue) -> {
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
timer.setText(newValue);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Countdown timer until race starts.
|
|
||||||
*/
|
|
||||||
private void countdownTimer() {
|
|
||||||
new AnimationTimer() {
|
|
||||||
@Override
|
|
||||||
public void handle(long arg0) {
|
|
||||||
|
|
||||||
//Get the current race status.
|
|
||||||
RaceStatusEnum raceStatus = visualiserRaceEvent.getVisualiserRaceState().getRaceStatusEnum();
|
|
||||||
|
|
||||||
//If the race has reached the preparatory phase, or has started...
|
|
||||||
if (raceStatus == RaceStatusEnum.WARNING
|
|
||||||
|| raceStatus == RaceStatusEnum.PREPARATORY
|
|
||||||
|| raceStatus == RaceStatusEnum.STARTED) {
|
|
||||||
//Stop this timer.
|
|
||||||
stop();
|
|
||||||
|
|
||||||
//Hide this, and display the race controller.
|
|
||||||
startWrapper.setVisible(false);
|
|
||||||
//start.setVisible(false);//TODO is this needed?
|
|
||||||
|
|
||||||
parent.beginRace(visualiserRaceEvent, controllerClient, isHost);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.start();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show starting information for a race given a socket.
|
|
||||||
* @param socket network source of information
|
|
||||||
* @param isHost is user a host
|
|
||||||
*/
|
|
||||||
public void enterLobby(Socket socket, Boolean isHost) {
|
|
||||||
try {
|
|
||||||
|
|
||||||
this.isHost = isHost;
|
|
||||||
|
|
||||||
this.visualiserRaceEvent = new VisualiserRaceEvent(socket, RequestToJoinEnum.PARTICIPANT);
|
|
||||||
|
|
||||||
this.controllerClient = visualiserRaceEvent.getControllerClient();
|
|
||||||
|
|
||||||
|
|
||||||
startWrapper.setVisible(true);
|
|
||||||
|
|
||||||
startRace();
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
|
||||||
//TODO should probably let this propagate, so that we only enter this scene if everything works
|
|
||||||
Logger.getGlobal().log(Level.WARNING, "Could not connect to server.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
package visualiser.app;
|
||||||
|
|
||||||
|
import visualiser.network.MatchBrowserInterface;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.DatagramSocket;
|
||||||
|
|
||||||
|
public class MatchBrowserSingleton {
|
||||||
|
private DatagramSocket udpSocket;
|
||||||
|
private MatchBrowserInterface matchBrowserInterface;
|
||||||
|
|
||||||
|
private static MatchBrowserSingleton instance = null;
|
||||||
|
|
||||||
|
public MatchBrowserSingleton() {
|
||||||
|
this.matchBrowserInterface = new MatchBrowserInterface();
|
||||||
|
try{
|
||||||
|
this.udpSocket = matchBrowserInterface.setupMatchBrowserConnection();
|
||||||
|
}catch (IOException e){
|
||||||
|
System.err.println("Error in setting up connection with match browser");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MatchBrowserSingleton getInstance() {
|
||||||
|
if (instance == null){
|
||||||
|
instance = new MatchBrowserSingleton();
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DatagramSocket getUdpSocket() {
|
||||||
|
return udpSocket;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MatchBrowserInterface getMatchBrowserInterface() {
|
||||||
|
return matchBrowserInterface;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
package visualiser.layout;
|
||||||
|
|
||||||
|
import javafx.scene.paint.Color;
|
||||||
|
import javafx.scene.paint.PhongMaterial;
|
||||||
|
import javafx.scene.shape.Cylinder;
|
||||||
|
import javafx.scene.transform.Rotate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by cbt24 on 14/09/17.
|
||||||
|
*/
|
||||||
|
public class Shockwave extends Subject3D {
|
||||||
|
public Shockwave(double radius) {
|
||||||
|
super(new Cylinder(radius,0),0);
|
||||||
|
getMesh().getTransforms().add(new Rotate(-90, Rotate.X_AXIS));
|
||||||
|
getMesh().setMaterial(new PhongMaterial(new Color(0,0,0,0)));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
package visualiser.network;
|
||||||
|
|
||||||
|
import network.Messages.AC35Data;
|
||||||
|
import shared.model.RunnableWithFramePeriod;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.DatagramSocket;
|
||||||
|
|
||||||
|
public class MatchBrowserClientRunnable implements RunnableWithFramePeriod {
|
||||||
|
private MatchBrowserLobbyInterface matchBrowserLobbyInterface;
|
||||||
|
private DatagramSocket socket;
|
||||||
|
|
||||||
|
public MatchBrowserClientRunnable(MatchBrowserLobbyInterface matchBrowserInterface, DatagramSocket socket) {
|
||||||
|
this.matchBrowserLobbyInterface = matchBrowserInterface;
|
||||||
|
this.socket = socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(){
|
||||||
|
long previousFrameTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
while (!Thread.interrupted()) {
|
||||||
|
try{
|
||||||
|
matchBrowserLobbyInterface.receiveGameInfo(socket);
|
||||||
|
}catch (IOException e){
|
||||||
|
System.err.println("HostGameMessage could not be received");
|
||||||
|
}
|
||||||
|
|
||||||
|
long currentFrameTime = System.currentTimeMillis();
|
||||||
|
waitForFramePeriod(previousFrameTime, currentFrameTime, 10000);
|
||||||
|
previousFrameTime = currentFrameTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
package visualiser.network;
|
||||||
|
|
||||||
|
import network.Exceptions.InvalidMessageException;
|
||||||
|
import network.Messages.AC35Data;
|
||||||
|
import network.Messages.HostGame;
|
||||||
|
import shared.model.RunnableWithFramePeriod;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.DatagramSocket;
|
||||||
|
|
||||||
|
|
||||||
|
public class MatchBrowserHostRunnable implements RunnableWithFramePeriod {
|
||||||
|
|
||||||
|
private MatchBrowserInterface matchBrowserInterface;
|
||||||
|
private DatagramSocket socket;
|
||||||
|
private AC35Data gameInfo;
|
||||||
|
|
||||||
|
public MatchBrowserHostRunnable(MatchBrowserInterface matchBrowserInterface, DatagramSocket socket, AC35Data gameInfo) {
|
||||||
|
this.matchBrowserInterface = matchBrowserInterface;
|
||||||
|
this.socket = socket;
|
||||||
|
this.gameInfo = gameInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(){
|
||||||
|
long previousFrameTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
while (!Thread.interrupted()) {
|
||||||
|
try{
|
||||||
|
matchBrowserInterface.sendOutGameInfo(gameInfo, socket);
|
||||||
|
}catch (IOException e){
|
||||||
|
System.err.println("HostGameMessage could not be sent");
|
||||||
|
}
|
||||||
|
|
||||||
|
long currentFrameTime = System.currentTimeMillis();
|
||||||
|
waitForFramePeriod(previousFrameTime, currentFrameTime, 10000);
|
||||||
|
previousFrameTime = currentFrameTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,93 @@
|
|||||||
|
package visualiser.network;
|
||||||
|
|
||||||
|
import network.BinaryMessageEncoder;
|
||||||
|
import network.Exceptions.InvalidMessageException;
|
||||||
|
import network.MessageEncoders.HostGameMessageEncoder;
|
||||||
|
import network.MessageEncoders.HostedGamesRequestEncoder;
|
||||||
|
import network.Messages.AC35Data;
|
||||||
|
import network.Messages.Enums.MessageType;
|
||||||
|
import network.Messages.HostGamesRequest;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.DatagramPacket;
|
||||||
|
import java.net.DatagramSocket;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UDP interface for the matchBrowser to send out hosted game info and get in other hosts info
|
||||||
|
*/
|
||||||
|
public class MatchBrowserInterface {
|
||||||
|
//ip of the server
|
||||||
|
private InetAddress IPAddress;
|
||||||
|
//port server is hosted on
|
||||||
|
private int port;
|
||||||
|
|
||||||
|
public MatchBrowserInterface() {
|
||||||
|
try {//132.181.16.13 is the ip of the CI as of 13/9/17
|
||||||
|
this.IPAddress = InetAddress.getByName("132.181.16.13"); //InetAddress.getLocalHost();
|
||||||
|
} catch (UnknownHostException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
this.port = 3779;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by host to send out race information to the server
|
||||||
|
* @param gameInfo the hostGame info for message
|
||||||
|
* @param socket the udp socket assigned on startup
|
||||||
|
* @throws IOException socket error
|
||||||
|
*/
|
||||||
|
protected void sendOutGameInfo(AC35Data gameInfo, DatagramSocket socket) throws IOException{
|
||||||
|
byte[] fullMessageToSend;
|
||||||
|
try{
|
||||||
|
HostGameMessageEncoder encoder = new HostGameMessageEncoder();
|
||||||
|
byte[] message = encoder.encode(gameInfo);
|
||||||
|
BinaryMessageEncoder messageEncoder = new BinaryMessageEncoder(MessageType.HOST_GAME
|
||||||
|
,System.currentTimeMillis(), 1,(short) 13 ,message);
|
||||||
|
fullMessageToSend = messageEncoder.getFullMessage();
|
||||||
|
|
||||||
|
DatagramPacket sendPacket = new DatagramPacket(fullMessageToSend, fullMessageToSend.length, IPAddress, port);
|
||||||
|
socket.send(sendPacket);
|
||||||
|
}catch (InvalidMessageException e){
|
||||||
|
System.err.println("HostGameMessage could not be encoded");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* start to send these messages on repeat until game stopped
|
||||||
|
* @param gameInfo hostgame data
|
||||||
|
* @param socket socket to send to
|
||||||
|
*/
|
||||||
|
public void startSendingHostData(AC35Data gameInfo, DatagramSocket socket){
|
||||||
|
MatchBrowserHostRunnable hostRunnable = new MatchBrowserHostRunnable(this, socket, gameInfo);
|
||||||
|
Thread hostRunnableThread = new Thread(hostRunnable, "Socket: " + socket.toString());
|
||||||
|
hostRunnableThread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by a client to setup a connection with the match browser server
|
||||||
|
* @return the socket created for this connection
|
||||||
|
* @throws IOException socket error
|
||||||
|
*/
|
||||||
|
public DatagramSocket setupMatchBrowserConnection() throws IOException{
|
||||||
|
DatagramSocket clientSocket = new DatagramSocket();
|
||||||
|
|
||||||
|
//creates and empty hostedGamesRequest packet that can be sent to the server
|
||||||
|
//this lets the server check the udp fields for ip and port to know that this client exists
|
||||||
|
try{
|
||||||
|
HostedGamesRequestEncoder encoder = new HostedGamesRequestEncoder();
|
||||||
|
byte[] message = encoder.encode(new HostGamesRequest(new ArrayList()));
|
||||||
|
BinaryMessageEncoder messageEncoder = new BinaryMessageEncoder(MessageType.HOSTED_GAMES_REQUEST
|
||||||
|
,System.currentTimeMillis(), 1,(short) 13 ,message);
|
||||||
|
|
||||||
|
DatagramPacket sendPacket = new DatagramPacket(messageEncoder.getFullMessage(), messageEncoder.getFullMessage().length, IPAddress, port);
|
||||||
|
clientSocket.send(sendPacket);
|
||||||
|
}catch (InvalidMessageException e){
|
||||||
|
System.err.println("HostedGamesRequestMessage could not be encoded");
|
||||||
|
}
|
||||||
|
|
||||||
|
return clientSocket;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,76 @@
|
|||||||
|
package visualiser.network;
|
||||||
|
|
||||||
|
import network.Exceptions.InvalidMessageException;
|
||||||
|
import network.MessageDecoders.HostedGamesRequestDecoder;
|
||||||
|
import network.Messages.HostGame;
|
||||||
|
import network.Messages.HostGamesRequest;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.DatagramPacket;
|
||||||
|
import java.net.DatagramSocket;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Observable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to receive lobby information from server
|
||||||
|
*/
|
||||||
|
public class MatchBrowserLobbyInterface extends Observable {
|
||||||
|
private DatagramSocket socket;
|
||||||
|
private MatchBrowserClientRunnable clientRunnable;
|
||||||
|
private List<HostGame> games = new ArrayList<>();
|
||||||
|
private Thread clientRunnableThread;
|
||||||
|
|
||||||
|
public MatchBrowserLobbyInterface() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* start receiving game info
|
||||||
|
* @param socket to receive from
|
||||||
|
*/
|
||||||
|
public void startReceivingHostData(DatagramSocket socket) {
|
||||||
|
this.socket = socket;
|
||||||
|
clientRunnable = new MatchBrowserClientRunnable(this, socket);
|
||||||
|
clientRunnableThread = new Thread(clientRunnable, "Socket: " + socket.toString());
|
||||||
|
clientRunnableThread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by client to received race information from the server
|
||||||
|
* @param socket socket to read from
|
||||||
|
* @throws IOException socket error
|
||||||
|
*/
|
||||||
|
protected void receiveGameInfo(DatagramSocket socket) throws IOException {
|
||||||
|
byte[] data = new byte[64];
|
||||||
|
DatagramPacket receivedPacket = new DatagramPacket(data, 64);
|
||||||
|
socket.receive(receivedPacket);
|
||||||
|
|
||||||
|
HostedGamesRequestDecoder hostedGamesRequestDecoder = new HostedGamesRequestDecoder();
|
||||||
|
try {
|
||||||
|
HostGamesRequest message = (HostGamesRequest) hostedGamesRequestDecoder.decode(data);
|
||||||
|
games = message.getKnownGames();
|
||||||
|
// System.out.println(games.get(0).getIp());
|
||||||
|
setChanged();
|
||||||
|
notifyObservers();
|
||||||
|
} catch (InvalidMessageException e) {
|
||||||
|
System.err.println("HostedGamesRequestMessage could not be decoded");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the host games
|
||||||
|
* @return games to be returned in list
|
||||||
|
*/
|
||||||
|
public List<HostGame> getGames() {
|
||||||
|
return games;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to close the socket and runnable once out of the lobby
|
||||||
|
*/
|
||||||
|
public void closeSocket() {
|
||||||
|
clientRunnableThread.interrupt();
|
||||||
|
this.socket.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,82 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
|
|
||||||
<?import javafx.geometry.Insets?>
|
|
||||||
<?import javafx.scene.control.Button?>
|
|
||||||
<?import javafx.scene.control.Label?>
|
|
||||||
<?import javafx.scene.control.TableColumn?>
|
|
||||||
<?import javafx.scene.control.TableView?>
|
|
||||||
<?import javafx.scene.control.TextField?>
|
|
||||||
<?import javafx.scene.layout.AnchorPane?>
|
|
||||||
<?import javafx.scene.layout.ColumnConstraints?>
|
|
||||||
<?import javafx.scene.layout.GridPane?>
|
|
||||||
<?import javafx.scene.layout.RowConstraints?>
|
|
||||||
<?import javafx.scene.text.Font?>
|
|
||||||
|
|
||||||
<AnchorPane fx:id="connectionWrapper" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="780.0" visible="false" xmlns="http://javafx.com/javafx/8.0.112" xmlns:fx="http://javafx.com/fxml/1" fx:controller="visualiser.Controllers.ConnectionController">
|
|
||||||
<children>
|
|
||||||
<GridPane fx:id="connection" prefHeight="600.0" prefWidth="780.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
|
||||||
<columnConstraints>
|
|
||||||
<ColumnConstraints hgrow="SOMETIMES" maxWidth="600.0" minWidth="10.0" prefWidth="600.0" />
|
|
||||||
<ColumnConstraints hgrow="SOMETIMES" maxWidth="600.0" minWidth="10.0" prefWidth="600.0" />
|
|
||||||
</columnConstraints>
|
|
||||||
<rowConstraints>
|
|
||||||
<RowConstraints maxHeight="182.0" minHeight="10.0" prefHeight="182.0" vgrow="SOMETIMES" />
|
|
||||||
<RowConstraints maxHeight="434.0" minHeight="10.0" prefHeight="434.0" vgrow="SOMETIMES" />
|
|
||||||
<RowConstraints maxHeight="174.0" minHeight="10.0" prefHeight="174.0" vgrow="SOMETIMES" />
|
|
||||||
<RowConstraints maxHeight="80.0" minHeight="50.0" prefHeight="80.0" vgrow="SOMETIMES" />
|
|
||||||
</rowConstraints>
|
|
||||||
<children>
|
|
||||||
<TableView fx:id="connectionTable" prefHeight="200.0" prefWidth="1080.0" GridPane.columnSpan="2" GridPane.rowIndex="1">
|
|
||||||
<columns>
|
|
||||||
<TableColumn fx:id="hostnameColumn" prefWidth="453.99998474121094" text="Host" />
|
|
||||||
<TableColumn fx:id="statusColumn" prefWidth="205.0" text="Status" />
|
|
||||||
</columns>
|
|
||||||
<GridPane.margin>
|
|
||||||
<Insets left="50.0" right="50.0" />
|
|
||||||
</GridPane.margin>
|
|
||||||
</TableView>
|
|
||||||
<Button mnemonicParsing="false" onAction="#checkConnections" text="Refresh" GridPane.halignment="RIGHT" GridPane.rowIndex="3">
|
|
||||||
<GridPane.margin>
|
|
||||||
<Insets right="20.0" />
|
|
||||||
</GridPane.margin>
|
|
||||||
</Button>
|
|
||||||
<Button fx:id="connectButton" mnemonicParsing="false" onAction="#connectSocket" text="Connect" GridPane.columnIndex="1" GridPane.halignment="LEFT" GridPane.rowIndex="3">
|
|
||||||
<GridPane.margin>
|
|
||||||
<Insets left="20.0" />
|
|
||||||
</GridPane.margin>
|
|
||||||
</Button>
|
|
||||||
<Label text="Welcome to RaceVision" GridPane.columnSpan="2" GridPane.halignment="CENTER">
|
|
||||||
<font>
|
|
||||||
<Font size="36.0" />
|
|
||||||
</font>
|
|
||||||
</Label>
|
|
||||||
<GridPane GridPane.columnSpan="2" GridPane.rowIndex="2">
|
|
||||||
<columnConstraints>
|
|
||||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
|
||||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
|
||||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
|
||||||
</columnConstraints>
|
|
||||||
<rowConstraints>
|
|
||||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
|
||||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
|
||||||
</rowConstraints>
|
|
||||||
<children>
|
|
||||||
<TextField fx:id="urlField" GridPane.rowIndex="1">
|
|
||||||
<GridPane.margin>
|
|
||||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
|
||||||
</GridPane.margin>
|
|
||||||
</TextField>
|
|
||||||
<TextField fx:id="portField" GridPane.columnIndex="1" GridPane.rowIndex="1">
|
|
||||||
<GridPane.margin>
|
|
||||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
|
||||||
</GridPane.margin>
|
|
||||||
</TextField>
|
|
||||||
<Button mnemonicParsing="false" onAction="#addConnection" text="Add New Connection" GridPane.columnIndex="2" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="CENTER" />
|
|
||||||
<Label text="Host Name:" GridPane.halignment="CENTER" GridPane.valignment="BOTTOM" />
|
|
||||||
<Label text="Port:" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.valignment="BOTTOM" />
|
|
||||||
</children>
|
|
||||||
</GridPane>
|
|
||||||
</children>
|
|
||||||
</GridPane>
|
|
||||||
</children>
|
|
||||||
</AnchorPane>
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
|
|
||||||
<?import javafx.scene.image.*?>
|
|
||||||
<?import java.lang.*?>
|
|
||||||
<?import javafx.scene.layout.*?>
|
|
||||||
|
|
||||||
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="350.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
|
|
||||||
<children>
|
|
||||||
<ImageView fitHeight="385.0" fitWidth="600.0" layoutY="-2.0" pickOnBounds="true" preserveRatio="true" AnchorPane.bottomAnchor="-1.9456787109375" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="-2.0">
|
|
||||||
<image>
|
|
||||||
<Image url="@../images/game_controls.png" />
|
|
||||||
</image>
|
|
||||||
</ImageView>
|
|
||||||
</children>
|
|
||||||
</AnchorPane>
|
|
||||||
@ -0,0 +1,112 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import java.lang.*?>
|
||||||
|
<?import javafx.geometry.*?>
|
||||||
|
<?import javafx.scene.control.*?>
|
||||||
|
<?import javafx.scene.image.*?>
|
||||||
|
<?import javafx.scene.layout.*?>
|
||||||
|
<?import javafx.scene.text.*?>
|
||||||
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import javafx.scene.control.Button?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.control.SplitPane?>
|
||||||
|
<?import javafx.scene.control.TableColumn?>
|
||||||
|
<?import javafx.scene.control.TableView?>
|
||||||
|
<?import javafx.scene.image.Image?>
|
||||||
|
<?import javafx.scene.image.ImageView?>
|
||||||
|
<?import javafx.scene.layout.AnchorPane?>
|
||||||
|
<?import javafx.scene.layout.ColumnConstraints?>
|
||||||
|
<?import javafx.scene.layout.GridPane?>
|
||||||
|
<?import javafx.scene.layout.RowConstraints?>
|
||||||
|
<?import javafx.scene.text.Font?>
|
||||||
|
|
||||||
|
<AnchorPane fx:id="gameLobbyWrapper" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="780.0" style="-fx-background-color: rgba(100, 100, 100, 0.2);" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="visualiser.Controllers.InGameLobbyController">
|
||||||
|
<children>
|
||||||
|
<GridPane style="-fx-background-color: rgba(0, 0, 0, 0.3);" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
|
<children>
|
||||||
|
<Button mnemonicParsing="false" onAction="#menuBtnPressed" text="Quit" GridPane.rowIndex="2">
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets left="20.0" />
|
||||||
|
</GridPane.margin>
|
||||||
|
</Button>
|
||||||
|
<Label fx:id="countdownLabel" alignment="CENTER" contentDisplay="CENTER" textFill="WHITE" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.rowIndex="2">
|
||||||
|
<font>
|
||||||
|
<Font size="16.0" />
|
||||||
|
</font>
|
||||||
|
</Label>
|
||||||
|
<GridPane fx:id="playerContainer" GridPane.columnSpan="3" GridPane.rowIndex="1">
|
||||||
|
<columnConstraints>
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
</columnConstraints>
|
||||||
|
<rowConstraints>
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
|
</rowConstraints>
|
||||||
|
<children>
|
||||||
|
<Label fx:id="playerLabel" text="No Player" GridPane.halignment="CENTER" GridPane.valignment="BOTTOM">
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0" />
|
||||||
|
</GridPane.margin>
|
||||||
|
</Label>
|
||||||
|
<Label fx:id="playerLabel4" text="No Player" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="BOTTOM">
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0" />
|
||||||
|
</GridPane.margin>
|
||||||
|
</Label>
|
||||||
|
<Label fx:id="playerLabel2" text="No Player" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.valignment="BOTTOM">
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0" />
|
||||||
|
</GridPane.margin>
|
||||||
|
</Label>
|
||||||
|
<Label fx:id="playerLabel3" text="No Player" GridPane.columnIndex="2" GridPane.halignment="CENTER" GridPane.valignment="BOTTOM">
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0" />
|
||||||
|
</GridPane.margin>
|
||||||
|
</Label>
|
||||||
|
<Label fx:id="playerLabel5" text="No Player" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="BOTTOM">
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0" />
|
||||||
|
</GridPane.margin>
|
||||||
|
</Label>
|
||||||
|
<Label fx:id="playerLabel6" text="No Player" GridPane.columnIndex="2" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="BOTTOM">
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0" />
|
||||||
|
</GridPane.margin>
|
||||||
|
</Label>
|
||||||
|
</children>
|
||||||
|
</GridPane>
|
||||||
|
<Label text="Get Ready For The Next Race" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.valignment="CENTER">
|
||||||
|
<font>
|
||||||
|
<Font name="Cabin-Bold" size="17.0" />
|
||||||
|
</font>
|
||||||
|
</Label>
|
||||||
|
</children>
|
||||||
|
<columnConstraints>
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
</columnConstraints>
|
||||||
|
<rowConstraints>
|
||||||
|
<RowConstraints maxHeight="80.0" minHeight="80.0" prefHeight="80.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints maxHeight="1.7976931348623157E308" minHeight="250.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints maxHeight="80.0" minHeight="80.0" prefHeight="80.0" vgrow="SOMETIMES" />
|
||||||
|
</rowConstraints>
|
||||||
|
</GridPane>
|
||||||
|
<AnchorPane fx:id="countdownTenPane" prefHeight="200.0" prefWidth="200.0" visible="false" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
|
<children>
|
||||||
|
<Label fx:id="countdownTenText" alignment="CENTER" text="COUNTDOWN" textFill="#ee0000" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
|
<font>
|
||||||
|
<Font name="System Bold" size="41.0" />
|
||||||
|
</font>
|
||||||
|
</Label>
|
||||||
|
<Label fx:id="preRacelabel" alignment="CENTER" text="Entering Race In:" textFill="RED" AnchorPane.bottomAnchor="80.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
|
<font>
|
||||||
|
<Font name="System Bold" size="18.0" />
|
||||||
|
</font>
|
||||||
|
</Label>
|
||||||
|
</children>
|
||||||
|
</AnchorPane>
|
||||||
|
</children>
|
||||||
|
</AnchorPane>
|
||||||
@ -0,0 +1,56 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import javafx.scene.control.Button?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.image.ImageView?>
|
||||||
|
<?import javafx.scene.layout.AnchorPane?>
|
||||||
|
<?import javafx.scene.layout.ColumnConstraints?>
|
||||||
|
<?import javafx.scene.layout.GridPane?>
|
||||||
|
<?import javafx.scene.layout.RowConstraints?>
|
||||||
|
<?import javafx.scene.text.Font?>
|
||||||
|
|
||||||
|
<AnchorPane fx:id="hostWrapper" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="780.0" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="visualiser.Controllers.HostGameController">
|
||||||
|
<children>
|
||||||
|
<GridPane layoutY="14.0" AnchorPane.bottomAnchor="-14.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="14.0">
|
||||||
|
<columnConstraints>
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" maxWidth="170.0" minWidth="10.0" prefWidth="170.0" />
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" maxWidth="170.0" minWidth="10.0" prefWidth="170.0" />
|
||||||
|
</columnConstraints>
|
||||||
|
<rowConstraints>
|
||||||
|
<RowConstraints maxHeight="60.0" minHeight="60.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="435.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints maxHeight="80.0" minHeight="80.0" prefHeight="80.0" vgrow="SOMETIMES" />
|
||||||
|
</rowConstraints>
|
||||||
|
<children>
|
||||||
|
<Button fx:id="hostGameBtn" mnemonicParsing="false" onAction="#hostGamePressed" text="Start Game" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.rowIndex="2" GridPane.valignment="CENTER">
|
||||||
|
<font>
|
||||||
|
<Font size="20.0" />
|
||||||
|
</font>
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
|
||||||
|
</GridPane.margin>
|
||||||
|
</Button>
|
||||||
|
<Label text="Address: 127.0.0.1" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.valignment="TOP">
|
||||||
|
<font>
|
||||||
|
<Font size="17.0" />
|
||||||
|
</font>
|
||||||
|
</Label>
|
||||||
|
<Label text="Port: 4942" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.valignment="BOTTOM">
|
||||||
|
<font>
|
||||||
|
<Font size="17.0" />
|
||||||
|
</font>
|
||||||
|
</Label>
|
||||||
|
<Button mnemonicParsing="false" onAction="#menuBtnPressed" text="Main Menu" GridPane.halignment="LEFT" GridPane.valignment="TOP">
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||||
|
</GridPane.margin>
|
||||||
|
</Button>
|
||||||
|
<ImageView fx:id="mapImage" pickOnBounds="true" preserveRatio="true" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.hgrow="ALWAYS" GridPane.rowIndex="1" GridPane.valignment="CENTER" GridPane.vgrow="ALWAYS" />
|
||||||
|
<Button fx:id="previousButton" maxHeight="80.0" maxWidth="80.0" mnemonicParsing="false" onAction="#previousImage" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="CENTER" />
|
||||||
|
<Button fx:id="nextButton" maxHeight="80.0" maxWidth="80.0" mnemonicParsing="false" onAction="#nextImage" GridPane.columnIndex="2" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="CENTER" />
|
||||||
|
</children>
|
||||||
|
</GridPane>
|
||||||
|
</children>
|
||||||
|
</AnchorPane>
|
||||||
@ -1,61 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
|
|
||||||
<?import java.lang.*?>
|
|
||||||
<?import javafx.geometry.*?>
|
|
||||||
<?import javafx.scene.control.*?>
|
|
||||||
<?import javafx.scene.image.*?>
|
|
||||||
<?import javafx.scene.layout.*?>
|
|
||||||
<?import javafx.scene.text.*?>
|
|
||||||
<?import javafx.geometry.Insets?>
|
|
||||||
<?import javafx.scene.control.Button?>
|
|
||||||
<?import javafx.scene.control.Label?>
|
|
||||||
<?import javafx.scene.image.ImageView?>
|
|
||||||
<?import javafx.scene.layout.AnchorPane?>
|
|
||||||
<?import javafx.scene.layout.ColumnConstraints?>
|
|
||||||
<?import javafx.scene.layout.GridPane?>
|
|
||||||
<?import javafx.scene.layout.RowConstraints?>
|
|
||||||
<?import javafx.scene.text.Font?>
|
|
||||||
|
|
||||||
<AnchorPane fx:id="hostWrapper" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="780.0" visible="false" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="visualiser.Controllers.HostController">
|
|
||||||
<children>
|
|
||||||
<GridPane layoutY="14.0" AnchorPane.bottomAnchor="-14.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="14.0">
|
|
||||||
<columnConstraints>
|
|
||||||
<ColumnConstraints hgrow="SOMETIMES" maxWidth="170.0" minWidth="10.0" prefWidth="170.0" />
|
|
||||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
|
||||||
<ColumnConstraints hgrow="SOMETIMES" maxWidth="170.0" minWidth="10.0" prefWidth="170.0" />
|
|
||||||
</columnConstraints>
|
|
||||||
<rowConstraints>
|
|
||||||
<RowConstraints maxHeight="60.0" minHeight="60.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
|
||||||
<RowConstraints minHeight="10.0" prefHeight="435.0" vgrow="SOMETIMES" />
|
|
||||||
<RowConstraints maxHeight="80.0" minHeight="80.0" prefHeight="80.0" vgrow="SOMETIMES" />
|
|
||||||
</rowConstraints>
|
|
||||||
<children>
|
|
||||||
<Button fx:id="hostGameBtn" mnemonicParsing="false" onAction="#hostGamePressed" text="Start Game" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.rowIndex="2" GridPane.valignment="CENTER">
|
|
||||||
<font>
|
|
||||||
<Font size="20.0" />
|
|
||||||
</font>
|
|
||||||
<GridPane.margin>
|
|
||||||
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
|
|
||||||
</GridPane.margin>
|
|
||||||
</Button>
|
|
||||||
<Label text="Address: 127.0.0.1" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.valignment="TOP">
|
|
||||||
<font>
|
|
||||||
<Font size="17.0" />
|
|
||||||
</font>
|
|
||||||
</Label>
|
|
||||||
<Label text="Port: 4942" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.valignment="BOTTOM">
|
|
||||||
<font>
|
|
||||||
<Font size="17.0" />
|
|
||||||
</font>
|
|
||||||
</Label>
|
|
||||||
<Button mnemonicParsing="false" onAction="#menuBtnPressed" text="Main Menu" GridPane.halignment="LEFT" GridPane.valignment="TOP">
|
|
||||||
<GridPane.margin>
|
|
||||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
|
||||||
</GridPane.margin></Button>
|
|
||||||
<ImageView fx:id="mapImage" pickOnBounds="true" preserveRatio="true" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.hgrow="ALWAYS" GridPane.rowIndex="1" GridPane.valignment="CENTER" GridPane.vgrow="ALWAYS" />
|
|
||||||
<Button fx:id="previousButton" maxHeight="80.0" maxWidth="80.0" mnemonicParsing="false" onAction="#previousImage" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="CENTER" />
|
|
||||||
<Button fx:id="nextButton" maxHeight="80.0" maxWidth="80.0" mnemonicParsing="false" onAction="#nextImage" GridPane.columnIndex="2" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="CENTER" />
|
|
||||||
</children>
|
|
||||||
</GridPane>
|
|
||||||
</children>
|
|
||||||
</AnchorPane>
|
|
||||||
@ -1,14 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
|
|
||||||
<?import javafx.scene.layout.AnchorPane?>
|
|
||||||
<AnchorPane fx:id="main" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="visualiser.Controllers.MainController">
|
|
||||||
<children>
|
|
||||||
<fx:include fx:id="race" source="race.fxml" />
|
|
||||||
<fx:include fx:id="start" source="start.fxml" />
|
|
||||||
<fx:include fx:id="connection" source="connect.fxml" />
|
|
||||||
<fx:include fx:id="finish" source="finish.fxml" />
|
|
||||||
<fx:include fx:id="host" source="hostgame.fxml" />
|
|
||||||
<fx:include fx:id="title" source="titleScreen.fxml" />
|
|
||||||
<fx:include fx:id="lobby" source="lobby.fxml" />
|
|
||||||
</children>
|
|
||||||
</AnchorPane>
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue