commit
e4a8bd08e7
@ -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,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,363 @@
|
||||
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, 250, 0, 210);
|
||||
subjects.add(sea.getSurface());
|
||||
|
||||
MeshView mesh = new MeshView(importer.getImport());
|
||||
Subject3D subject = new Subject3D(mesh);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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,124 @@
|
||||
package visualiser.layout;
|
||||
|
||||
import javafx.scene.shape.TriangleMesh;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 3D plane
|
||||
*/
|
||||
public class Plane3D extends TriangleMesh{
|
||||
|
||||
/**
|
||||
* Length is up down, and width is left right. Drawn on the x-y plane with z kept at 0.
|
||||
* @param width width of the plane
|
||||
* @param length length of the plane
|
||||
* @param subdivisionsWidth number of divisions along the width of the plane
|
||||
* @param subdivisionsLength number of division along the length of the plane
|
||||
*/
|
||||
public Plane3D(float width, float length, int subdivisionsWidth, int subdivisionsLength){
|
||||
//add texture points and vertex points
|
||||
float subWidth = width / (float) subdivisionsWidth;
|
||||
float subLength = length / (float) subdivisionsLength;
|
||||
|
||||
ArrayList<Float> pointsList = new ArrayList<>();
|
||||
ArrayList<Float> textureCoord = new ArrayList<>();
|
||||
float startW = -width/2;
|
||||
float startL = -length/2;
|
||||
|
||||
for (float l = 0; l <= length; l += subLength) {
|
||||
for (float w = 0; w <= width; w += subWidth){
|
||||
//add points
|
||||
pointsList.add(w + startW);
|
||||
pointsList.add(l + startL);
|
||||
pointsList.add(0f);
|
||||
//addTexture coords
|
||||
textureCoord.add(1 - w/width);
|
||||
textureCoord.add(1 - l/length);
|
||||
}
|
||||
}
|
||||
|
||||
this.getPoints().setAll(copyListToArray(pointsList));
|
||||
this.getTexCoords().setAll(copyListToArray(textureCoord));
|
||||
|
||||
//connect points to make faces
|
||||
ArrayList<Integer> faces = new ArrayList<>();
|
||||
|
||||
int listSize = pointsList.size()/3;
|
||||
int divsInRow = subdivisionsWidth + 1;
|
||||
for (int i = 0; i < listSize; i++){
|
||||
int row = i/divsInRow;
|
||||
|
||||
if (row < 1){
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean notFirstCol = (i) % divsInRow != 0;
|
||||
boolean notLastCol = (i + 1) % divsInRow != 0;
|
||||
if (notFirstCol){
|
||||
faces.add(i);
|
||||
faces.add(i);
|
||||
// printPointAtIndex(i);
|
||||
faces.add(i - divsInRow);
|
||||
faces.add(i - divsInRow);
|
||||
// printPointAtIndex(i - divsInRow);
|
||||
faces.add(i - 1);
|
||||
faces.add(i - 1);
|
||||
// printPointAtIndex(i-1);
|
||||
}
|
||||
if (notLastCol) {
|
||||
faces.add(i - divsInRow + 1);
|
||||
faces.add(i - divsInRow + 1);
|
||||
// printPointAtIndex(i - divsInRow + 1);
|
||||
faces.add(i - divsInRow);
|
||||
faces.add(i - divsInRow);
|
||||
// printPointAtIndex(i - divsInRow);
|
||||
faces.add(i);
|
||||
faces.add(i);
|
||||
// printPointAtIndex(i);
|
||||
}
|
||||
|
||||
}
|
||||
this.getFaces().setAll(copyListToIntArray(faces));
|
||||
}
|
||||
|
||||
/**
|
||||
* Testing function to see if the points are correct
|
||||
* @param index index that the points correspond to (remember 3 is a point)
|
||||
*/
|
||||
private void printPointAtIndex(int index){
|
||||
int i = index * 3;
|
||||
float x = this.getPoints().get(i);
|
||||
float y = this.getPoints().get(i + 1);
|
||||
float z = this.getPoints().get(i + 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* copies the list to a float array because java List.toArray isn't working
|
||||
* @param list list to copy
|
||||
* @return array
|
||||
*/
|
||||
private static float[] copyListToArray(List<Float> list){
|
||||
float[] res = new float[list.size()];
|
||||
for (int i = 0; i < list.size(); i++){
|
||||
res[i] = list.get(i);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* copies the list to an integer array because java List.toArray isn't working
|
||||
* @param list list to copy
|
||||
* @return array
|
||||
*/
|
||||
private static int[] copyListToIntArray(List<Integer> list){
|
||||
int[] res = new int[list.size()];
|
||||
for (int i = 0; i < list.size(); i++){
|
||||
res[i] = list.get(i);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,136 @@
|
||||
package visualiser.layout;
|
||||
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.PixelWriter;
|
||||
import javafx.scene.image.WritableImage;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.paint.PhongMaterial;
|
||||
import javafx.scene.shape.MeshView;
|
||||
import visualiser.utils.PerlinNoiseGenerator;
|
||||
|
||||
/**
|
||||
* Creates a SeaSurface
|
||||
*/
|
||||
public class SeaSurface {
|
||||
private float[][] noiseArray;
|
||||
private Subject3D surface;
|
||||
|
||||
/**
|
||||
* Sea Surface Constructor
|
||||
* @param size size of the sea surface (has to be square for simplicity's sake)
|
||||
* @param freq frequency the perlin noise is to be generated at
|
||||
* @param x offset that the sea should be set at position-wise
|
||||
* @param y offset that the sea should be set at position-wise
|
||||
* @param z offset that the sea should be set at position-wise
|
||||
*/
|
||||
public SeaSurface(int size, double freq, double x, double y, double z){
|
||||
noiseArray = PerlinNoiseGenerator.createNoise(size, freq);
|
||||
createSurface();
|
||||
surface.setZ(z);
|
||||
surface.setY(y);
|
||||
surface.setX(x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the sea surface
|
||||
*/
|
||||
private void createSurface(){
|
||||
Image diffuseMap = createImage(noiseArray.length, noiseArray);
|
||||
|
||||
PhongMaterial material = new PhongMaterial();
|
||||
material.setDiffuseMap(diffuseMap);
|
||||
//material.setSpecularColor(Color.WHITE);
|
||||
|
||||
Plane3D seaPlane = new Plane3D(noiseArray.length, noiseArray.length, 10, 10);
|
||||
MeshView seaSurface = new MeshView(seaPlane);
|
||||
// Box seaSurface = new Box(noiseArray.length, 0.1, noiseArray.length);
|
||||
seaSurface.setMaterial(material);
|
||||
seaSurface.setMouseTransparent(true);
|
||||
seaSurface.toFront();
|
||||
//seaSurface.setRotationAxis(new Point3D(1, 0, 0));
|
||||
//seaSurface.setRotate(90);
|
||||
|
||||
surface = new Subject3D(seaSurface);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create texture for uv mapping
|
||||
* @param size size of the image to make
|
||||
* @param noise array of noise
|
||||
* @return image that is created
|
||||
*/
|
||||
private Image createImage(double size, float[][] noise) {
|
||||
|
||||
int width = (int) size;
|
||||
int height = (int) size;
|
||||
|
||||
WritableImage wr = new WritableImage(width, height);
|
||||
PixelWriter pw = wr.getPixelWriter();
|
||||
//interpolate colours based on noise
|
||||
for (int x = 0; x < width; x++) {
|
||||
for (int y = 0; y < height; y++) {
|
||||
|
||||
float value = noise[x][y];
|
||||
|
||||
double gray = normalizeValue(value, -.5, .5, 0., 1.);
|
||||
|
||||
gray = clamp(gray, 0, 1);
|
||||
|
||||
//values to interpolate on
|
||||
Color brightBlue = new Color(0.06, 0.5, .78, 1);
|
||||
Color lightBlue = new Color(0.15, 0.68, .88, 1);
|
||||
Color lighterBlue = new Color(0.28, 0.73, .91, 1);
|
||||
|
||||
Color colour = Color.WHITE.interpolate(brightBlue, gray).interpolate(lighterBlue, gray).interpolate(lightBlue, gray);
|
||||
|
||||
pw.setColor(x, y, colour);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return wr;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Nomalises the values so that the colours are correct
|
||||
* @param value value to normalise
|
||||
* @param min current min
|
||||
* @param max current max
|
||||
* @param newMin new min
|
||||
* @param newMax new max
|
||||
* @return returns normalised value
|
||||
*/
|
||||
private static double normalizeValue(double value, double min, double max, double newMin, double newMax) {
|
||||
|
||||
return (value - min) * (newMax - newMin) / (max - min) + newMin;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* clamps a value between a min and max
|
||||
* @param value value to clamp
|
||||
* @param min minimum value it can be
|
||||
* @param max maximum value it can be
|
||||
* @return result after clamp
|
||||
*/
|
||||
private static double clamp(double value, double min, double max) {
|
||||
|
||||
if (Double.compare(value, min) < 0)
|
||||
return min;
|
||||
|
||||
if (Double.compare(value, max) > 0)
|
||||
return max;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get surface
|
||||
* @return the surface so it can be drawn
|
||||
*/
|
||||
public Subject3D getSurface(){
|
||||
return surface;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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,94 @@
|
||||
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() {
|
||||
//TODO change to ip of cloud server hosting this
|
||||
try {
|
||||
this.IPAddress = InetAddress.getLocalHost();//InetAddress.getByName("132.181.13.223");
|
||||
} 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();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,88 @@
|
||||
package visualiser.utils;
|
||||
|
||||
/**
|
||||
* Perlin Noise Generator
|
||||
*/
|
||||
public class PerlinNoiseGenerator {
|
||||
|
||||
|
||||
/**
|
||||
* Create an array of the given size with values of perlin noise
|
||||
* @param size size of array that you wish to create
|
||||
* @param freq frequency that the noise is to be generated at.
|
||||
* @return noise generated
|
||||
*/
|
||||
public static float[][] createNoise( int size, double freq) {
|
||||
float[][] noiseArray = new float[(int) size][(int) size];
|
||||
|
||||
for (int x = 0; x < size; x++) {
|
||||
for (int y = 0; y < size; y++) {
|
||||
|
||||
double frequency = freq / (double) size;
|
||||
|
||||
double noise = ImprovedNoise.noise(x * frequency, y * frequency, 0);
|
||||
|
||||
noiseArray[x][y] = (float) noise;
|
||||
}
|
||||
}
|
||||
|
||||
return noiseArray;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Perlin noise generator
|
||||
*
|
||||
* // JAVA REFERENCE IMPLEMENTATION OF IMPROVED NOISE - COPYRIGHT 2002 KEN PERLIN.
|
||||
* // http://mrl.nyu.edu/~perlin/paper445.pdf
|
||||
* // http://mrl.nyu.edu/~perlin/noise/
|
||||
*/
|
||||
public final static class ImprovedNoise {
|
||||
static public double noise(double x, double y, double z) {
|
||||
int X = (int)Math.floor(x) & 255, // FIND UNIT CUBE THAT
|
||||
Y = (int)Math.floor(y) & 255, // CONTAINS POINT.
|
||||
Z = (int)Math.floor(z) & 255;
|
||||
x -= Math.floor(x); // FIND RELATIVE X,Y,Z
|
||||
y -= Math.floor(y); // OF POINT IN CUBE.
|
||||
z -= Math.floor(z);
|
||||
double u = fade(x), // COMPUTE FADE CURVES
|
||||
v = fade(y), // FOR EACH OF X,Y,Z.
|
||||
w = fade(z);
|
||||
int A = p[X ]+Y, AA = p[A]+Z, AB = p[A+1]+Z, // HASH COORDINATES OF
|
||||
B = p[X+1]+Y, BA = p[B]+Z, BB = p[B+1]+Z; // THE 8 CUBE CORNERS,
|
||||
|
||||
return lerp(w, lerp(v, lerp(u, grad(p[AA ], x , y , z ), // AND ADD
|
||||
grad(p[BA ], x-1, y , z )), // BLENDED
|
||||
lerp(u, grad(p[AB ], x , y-1, z ), // RESULTS
|
||||
grad(p[BB ], x-1, y-1, z ))),// FROM 8
|
||||
lerp(v, lerp(u, grad(p[AA+1], x , y , z-1 ), // CORNERS
|
||||
grad(p[BA+1], x-1, y , z-1 )), // OF CUBE
|
||||
lerp(u, grad(p[AB+1], x , y-1, z-1 ),
|
||||
grad(p[BB+1], x-1, y-1, z-1 ))));
|
||||
}
|
||||
static double fade(double t) { return t * t * t * (t * (t * 6 - 15) + 10); }
|
||||
static double lerp(double t, double a, double b) { return a + t * (b - a); }
|
||||
static double grad(int hash, double x, double y, double z) {
|
||||
int h = hash & 15; // CONVERT LO 4 BITS OF HASH CODE
|
||||
double u = h<8 ? x : y, // INTO 12 GRADIENT DIRECTIONS.
|
||||
v = h<4 ? y : h==12||h==14 ? x : z;
|
||||
return ((h&1) == 0 ? u : -u) + ((h&2) == 0 ? v : -v);
|
||||
}
|
||||
static final int p[] = new int[512], permutation[] = { 151,160,137,91,90,15,
|
||||
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
|
||||
190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
|
||||
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
|
||||
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
|
||||
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
|
||||
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
|
||||
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
|
||||
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
|
||||
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
|
||||
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
|
||||
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
|
||||
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
|
||||
};
|
||||
static { for (int i=0; i < 256 ; i++) p[256+i] = p[i] = permutation[i]; }
|
||||
}
|
||||
|
||||
}
|
||||
@ -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,36 @@
|
||||
package network.MessageDecoders;
|
||||
|
||||
import network.MessageEncoders.HostGameMessageEncoder;
|
||||
import network.Messages.AC35Data;
|
||||
import network.Messages.Enums.RaceStatusEnum;
|
||||
import network.Messages.HostGame;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class HostGameMessageDecoderTest {
|
||||
|
||||
@Test
|
||||
public void hostGameMessageDecoderTest() throws Exception {
|
||||
HostGame testHost = new HostGame("127.0.0.1", 3779, (byte) 1, (byte) 2, RaceStatusEnum.PRESTART, (byte) 6, (byte) 2);
|
||||
|
||||
|
||||
HostGameMessageEncoder encoder = new HostGameMessageEncoder();
|
||||
|
||||
byte[] encodedMessage = encoder.encode(testHost);
|
||||
|
||||
HostGameMessageDecoder decoder = new HostGameMessageDecoder();
|
||||
|
||||
HostGame decodedTest = (HostGame) decoder.decode(encodedMessage);
|
||||
|
||||
compareHostGameMessage(testHost, decodedTest);
|
||||
}
|
||||
|
||||
public static void compareHostGameMessage(HostGame original, HostGame decoded) {
|
||||
Assert.assertEquals(original.getIp(), decoded.getIp());
|
||||
Assert.assertEquals(original.getPort(), decoded.getPort());
|
||||
Assert.assertEquals(original.getSpeed(), decoded.getSpeed());
|
||||
Assert.assertEquals(original.getStatus(), decoded.getStatus());
|
||||
Assert.assertEquals(original.getRequiredNumPlayers(), decoded.getRequiredNumPlayers());
|
||||
Assert.assertEquals(original.getCurrentNumPlayers(), decoded.getCurrentNumPlayers());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
package network.MessageDecoders;
|
||||
|
||||
|
||||
import network.MessageEncoders.HostGameMessageEncoder;
|
||||
import network.MessageEncoders.HostedGamesRequestEncoder;
|
||||
import network.Messages.Enums.RaceStatusEnum;
|
||||
import network.Messages.HostGame;
|
||||
import network.Messages.HostGamesRequest;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class HostedGamesRequestDecoderTest {
|
||||
@Test
|
||||
public void hostGamesRequestMessageDecoderTest() throws Exception {
|
||||
HostGame testHostGame1 = new HostGame("127.0.0.1", 3779, (byte) 1, (byte) 2, RaceStatusEnum.PRESTART, (byte) 6, (byte) 2);
|
||||
HostGame testHostGame2 = new HostGame("127.0.0.1", 3780, (byte) 1, (byte) 2, RaceStatusEnum.PRESTART, (byte) 6, (byte) 2);
|
||||
|
||||
List knownGames = Arrays.asList(testHostGame1, testHostGame2);
|
||||
|
||||
HostedGamesRequestEncoder encoder = new HostedGamesRequestEncoder();
|
||||
|
||||
byte[] encodedMessage = encoder.encode(new HostGamesRequest(knownGames));
|
||||
|
||||
HostedGamesRequestDecoder decoder = new HostedGamesRequestDecoder();
|
||||
|
||||
HostGamesRequest decodedTest = (HostGamesRequest) decoder.decode(encodedMessage);
|
||||
|
||||
compareHostGamesRequestMessage(knownGames, decodedTest.getKnownGames());
|
||||
}
|
||||
|
||||
public static void compareHostGamesRequestMessage(List<HostGame> original, List<HostGame> decoded) {
|
||||
Assert.assertEquals(original.get(0).getIp(), decoded.get(0).getIp());
|
||||
Assert.assertEquals(original.get(1).getPort(), decoded.get(1).getPort());
|
||||
}
|
||||
}
|
||||
Loading…
Reference in new issue