Merge branch 'develop' into 1250_SendingGameObjects

# Conflicts:
#	src/main/java/seng302/gameServer/GameState.java
#	src/main/java/seng302/gameServer/MainServerThread.java
#	src/main/java/seng302/visualiser/GameView.java
#	src/main/java/seng302/visualiser/controllers/RaceViewController.java
This commit is contained in:
William Muir
2017-09-09 14:02:12 +12:00
42 changed files with 1036 additions and 279 deletions
@@ -17,6 +17,7 @@ import org.w3c.dom.Document;
import org.xml.sax.InputSource; import org.xml.sax.InputSource;
import seng302.gameServer.messages.BoatAction; import seng302.gameServer.messages.BoatAction;
import seng302.gameServer.messages.BoatStatus; import seng302.gameServer.messages.BoatStatus;
import seng302.gameServer.messages.ChatterMessage;
import seng302.gameServer.messages.CustomizeRequestType; import seng302.gameServer.messages.CustomizeRequestType;
import seng302.gameServer.messages.MarkRoundingMessage; import seng302.gameServer.messages.MarkRoundingMessage;
import seng302.gameServer.messages.MarkType; import seng302.gameServer.messages.MarkType;
@@ -45,7 +46,6 @@ public class GameState implements Runnable {
@FunctionalInterface @FunctionalInterface
interface NewMessageListener { interface NewMessageListener {
void notify(Message message); void notify(Message message);
} }
@@ -70,6 +70,7 @@ public class GameState implements Runnable {
private static Long previousUpdateTime; private static Long previousUpdateTime;
public static Double windDirection; public static Double windDirection;
private static Double windSpeed; private static Double windSpeed;
private static Double speedMultiplier = 1d;
private static Boolean customizationFlag; // dirty flag to tell if a player has customized their boat. private static Boolean customizationFlag; // dirty flag to tell if a player has customized their boat.
@@ -99,7 +100,7 @@ public class GameState implements Runnable {
players = new ArrayList<>(); players = new ArrayList<>();
GameState.hostIpAddress = hostIpAddress; GameState.hostIpAddress = hostIpAddress;
customizationFlag = false; customizationFlag = false;
speedMultiplier = 1.0;
currentStage = GameStages.LOBBYING; currentStage = GameStages.LOBBYING;
isRaceStarted = false; isRaceStarted = false;
//set this when game stage changes to prerace //set this when game stage changes to prerace
@@ -431,7 +432,7 @@ public class GameState implements Runnable {
private void updateVelocity(ServerYacht yacht) { private void updateVelocity(ServerYacht yacht) {
Double trueWindAngle = Math.abs(windDirection - yacht.getHeading()); Double trueWindAngle = Math.abs(windDirection - yacht.getHeading());
Double boatSpeedInKnots = PolarTable.getBoatSpeed(getWindSpeedKnots(), trueWindAngle); Double boatSpeedInKnots = PolarTable.getBoatSpeed(getWindSpeedKnots(), trueWindAngle);
Double maxBoatSpeed = GeoUtility.knotsToMMS(boatSpeedInKnots); Double maxBoatSpeed = GeoUtility.knotsToMMS(boatSpeedInKnots) * speedMultiplier;
if (yacht.getPowerUp() != null) { if (yacht.getPowerUp() != null) {
if (yacht.getPowerUp().equals(TokenType.BOOST)) { if (yacht.getPowerUp().equals(TokenType.BOOST)) {
maxBoatSpeed *= 2; maxBoatSpeed *= 2;
@@ -756,6 +757,35 @@ public class GameState implements Runnable {
} }
public static void processChatter(ChatterMessage chatterMessage, boolean isHost) {
String chatterText = chatterMessage.getMessage();
String[] words = chatterText.split("\\s+");
if (words.length > 2 && isHost) {
switch (words[2].trim()) {
case ">speed":
try {
setSpeedMultiplier(Double.valueOf(words[3]));
notifyMessageListeners(new ChatterMessage(
chatterMessage.getMessage_type(),
"SERVER: Speed modifier set to x" + words[3]
));
} catch (Exception e) {
Logger logger = LoggerFactory.getLogger(GameState.class);
logger.error("cannot parse >speed value");
}
return;
case ">finish":
notifyMessageListeners(new ChatterMessage(
chatterMessage.getMessage_type(),
"SERVER: Game will now finish"
));
endRace();
return;
}
}
notifyMessageListeners(chatterMessage);
}
public static void addMessageEventListener(NewMessageListener listener) { public static void addMessageEventListener(NewMessageListener listener) {
markListeners.add(listener); markListeners.add(listener);
} }
@@ -772,4 +802,16 @@ public class GameState implements Runnable {
customizationFlag = false; customizationFlag = false;
} }
public static void endRace () {
yachts.forEach((id, yacht) -> yacht.setBoatStatus(BoatStatus.FINISHED));
currentStage = GameStages.FINISHED;
}
public static void setSpeedMultiplier (double multiplier) {
speedMultiplier = multiplier;
}
public static double getSpeedMultiplier () {
return speedMultiplier;
}
} }
@@ -4,6 +4,8 @@ import java.io.IOException;
import java.util.Stack; import java.util.Stack;
import java.util.Timer; import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import seng302.model.Player; import seng302.model.Player;
import seng302.gameServer.messages.Heartbeat; import seng302.gameServer.messages.Heartbeat;
import seng302.gameServer.messages.Message; import seng302.gameServer.messages.Message;
@@ -14,6 +16,9 @@ import seng302.gameServer.messages.Message;
* cannot be sent to a player * cannot be sent to a player
*/ */
public class HeartbeatThread implements Runnable { public class HeartbeatThread implements Runnable {
private Logger logger = LoggerFactory.getLogger(HeartbeatThread.class);
private final int HEARTBEAT_PERIOD = 200; private final int HEARTBEAT_PERIOD = 200;
private ClientConnectionDelegate delegate; private ClientConnectionDelegate delegate;
private Integer seqNum; private Integer seqNum;
@@ -44,12 +49,12 @@ public class HeartbeatThread implements Runnable {
* The delegate is notified if a player has disconnected * The delegate is notified if a player has disconnected
*/ */
private void sendHeartbeatToAllPlayers(){ private void sendHeartbeatToAllPlayers(){
try {
Message heartbeat = new Heartbeat(seqNum); Message heartbeat = new Heartbeat(seqNum);
for (Player player : GameState.getPlayers()){ for (Player player : GameState.getPlayers()) {
if (!player.getSocket().isConnected()) { if (!player.getSocket().isConnected()) {
playerLostConnection(player); playerLostConnection(player);
} }
try { try {
player.getSocket().getOutputStream().write(heartbeat.getBuffer()); player.getSocket().getOutputStream().write(heartbeat.getBuffer());
} catch (IOException e) { } catch (IOException e) {
@@ -58,6 +63,9 @@ public class HeartbeatThread implements Runnable {
} }
updateDelegate(); updateDelegate();
seqNum++; seqNum++;
} catch (NullPointerException ne) {
logger.debug("Socket closed between checking for connection and sending heartbeat");
}
} }
/** /**
@@ -85,17 +85,20 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate {
//FINISHED //FINISHED
else if (GameState.getCurrentStage() == GameStages.FINISHED) { else if (GameState.getCurrentStage() == GameStages.FINISHED) {
broadcastMessage(makeRaceStatusMessage());
try {
Thread.sleep(1000); //Hackish fix to make sure all threads have sent closing RaceStatus
terminate(); terminate();
} catch (InterruptedException ie) {
serverLog("Thread interrupted while waiting to terminate clients", 1);
}
} }
} }
// TODO: 14/07/17 wmu16 - Send out disconnect packet to clients
try { try {
for (ServerToClientThread serverToClientThread : serverToClientThreads) { for (ServerToClientThread serverToClientThread : serverToClientThreads) {
serverToClientThread.terminate(); serverToClientThread.terminate();
} }
serverSocket.close(); serverSocket.close();
return;
} catch (IOException e) { } catch (IOException e) {
System.out.println("IO error in server thread handler upon closing socket"); System.out.println("IO error in server thread handler upon closing socket");
} }
@@ -199,6 +202,9 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate {
@Override @Override
public void clientConnected(ServerToClientThread serverToClientThread) { public void clientConnected(ServerToClientThread serverToClientThread) {
logger.debug("Player Connected From " + serverToClientThread.getThread().getName(), 0); logger.debug("Player Connected From " + serverToClientThread.getThread().getName(), 0);
if (serverToClientThreads.size() == 0) { //Sets first client as host.
serverToClientThread.setAsHost();
}
serverToClientThreads.add(serverToClientThread); serverToClientThreads.add(serverToClientThread);
serverToClientThread.addConnectionListener(this::sendSetupMessages); serverToClientThread.addConnectionListener(this::sendSetupMessages);
serverToClientThread.addDisconnectListener(this::clientDisconnected); serverToClientThread.addDisconnectListener(this::clientDisconnected);
@@ -2,6 +2,7 @@ package seng302.gameServer;
import java.util.Arrays; import java.util.Arrays;
import seng302.gameServer.messages.BoatAction; import seng302.gameServer.messages.BoatAction;
import seng302.gameServer.messages.ChatterMessage;
import seng302.gameServer.messages.ClientType; import seng302.gameServer.messages.ClientType;
import seng302.gameServer.messages.CustomizeRequestType; import seng302.gameServer.messages.CustomizeRequestType;
import seng302.gameServer.messages.Message; import seng302.gameServer.messages.Message;
@@ -28,5 +29,18 @@ public class ServerPacketParser {
long type = Message.bytesToLong(Arrays.copyOfRange(payload, 4, 5)); long type = Message.bytesToLong(Arrays.copyOfRange(payload, 4, 5));
return CustomizeRequestType.getRequestType((int) type); return CustomizeRequestType.getRequestType((int) type);
} }
public static ChatterMessage extractChatterText(byte[] payload) {
return new ChatterMessage(
payload[1], new String(Arrays.copyOfRange(payload, 3, payload.length))
);
}
public static ChatterMessage extractChatterText(StreamPacket packet) {
byte[] payload = packet.getPayload();
return new ChatterMessage(
payload[1], new String(Arrays.copyOfRange(payload, 3, payload.length))
);
}
} }
@@ -22,6 +22,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import seng302.gameServer.messages.BoatAction; import seng302.gameServer.messages.BoatAction;
import seng302.gameServer.messages.BoatLocationMessage; import seng302.gameServer.messages.BoatLocationMessage;
import seng302.gameServer.messages.ChatterMessage;
import seng302.gameServer.messages.ClientType; import seng302.gameServer.messages.ClientType;
import seng302.gameServer.messages.CustomizeRequestType; import seng302.gameServer.messages.CustomizeRequestType;
import seng302.gameServer.messages.Message; import seng302.gameServer.messages.Message;
@@ -75,6 +76,7 @@ public class ServerToClientThread implements Runnable {
private ClientType clientType; private ClientType clientType;
private Boolean isRegistered = false; private Boolean isRegistered = false;
private Boolean isHost = false;
private XMLGenerator xmlGenerator; private XMLGenerator xmlGenerator;
@@ -199,7 +201,12 @@ public class ServerToClientThread implements Runnable {
completeRegistration(requestedType); completeRegistration(requestedType);
break; break;
case CHATTER_TEXT:
ChatterMessage chatterMessage = ServerPacketParser
.extractChatterText(
new StreamPacket(type, payloadLength, timeStamp, payload));
GameState.processChatter(chatterMessage, isHost);
break;
case RACE_CUSTOMIZATION_REQUEST: case RACE_CUSTOMIZATION_REQUEST:
Long sourceID = Message Long sourceID = Message
.bytesToLong(Arrays.copyOfRange(payload, 0, 3)); .bytesToLong(Arrays.copyOfRange(payload, 0, 3));
@@ -313,4 +320,41 @@ public class ServerToClientThread implements Runnable {
public void addDisconnectListener(DisconnectListener disconnectListener) { public void addDisconnectListener(DisconnectListener disconnectListener) {
this.disconnectListener = disconnectListener; this.disconnectListener = disconnectListener;
} }
public void setAsHost() {
isHost = true;
}
private void checkChatterForCommands(ChatterMessage chatterMessage) {
// String chatterText = new String(
// Arrays.copyOfRange(chatterPayload, 3, 3 + chatterPayload.length)
// );
// String[] words = chatterText.split("\\s+");
// if (words.length > 2 && isHost) {
// switch (words[2].trim()) {
// case ">speed":
// try {
// GameState.setSpeedMultiplier(Double.valueOf(words[3]));
// GameState.broadcastChatter(new ChatterMessage(
// Byte.toUnsignedInt(chatterPayload[1]),
// "SERVER: Speed modifier set to x" + words[3]
// ));
// } catch (Exception e) {
// logger.error("cannot parse >speed value");
// }
// return;
// case ">finish":
// GameState.broadcastChatter(new ChatterMessage(
// chatterPayload[1],
// "SERVER: Game will now finish"
// ));
// GameState.endRace();
// return;
// }
// }
// GameState.broadcastChatter(
// ServerPacketParser.extractChatterText(chatterPayload)
// );
}
} }
@@ -11,9 +11,11 @@ public class ChatterMessage extends Message {
private int message_size = 21; private int message_size = 21;
private String message; private String message;
public ChatterMessage(int message_type, int message_size, String message) { public ChatterMessage(int message_type, String message) {
byte[] byteMessage = message.getBytes();
this.message_type = message_type; this.message_type = message_type;
this.message_size = message_size; this.message_size = byteMessage.length;
this.message = message; this.message = message;
setHeader(new Header(MessageType.CHATTER_TEXT, 1, (short) getSize())); setHeader(new Header(MessageType.CHATTER_TEXT, 1, (short) getSize()));
@@ -23,7 +25,7 @@ public class ChatterMessage extends Message {
putByte((byte) MESSAGE_VERSION_NUMBER); putByte((byte) MESSAGE_VERSION_NUMBER);
putInt(message_type, 1); putInt(message_type, 1);
putInt(message_size, 1); putInt(message_size, 1);
putBytes(message.getBytes()); putBytes(byteMessage);
writeCRC(); writeCRC();
rewind(); rewind();
@@ -34,5 +36,11 @@ public class ChatterMessage extends Message {
return MESSAGE_SIZE + message_size; return MESSAGE_SIZE + message_size;
} }
public String getMessage() {
return message;
}
public int getMessage_type() {
return message_type;
}
} }
@@ -15,6 +15,7 @@ import javafx.collections.FXCollections;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import seng302.model.stream.parser.RaceStartData; import seng302.model.stream.parser.RaceStartData;
import seng302.model.stream.parser.RaceStatusData; import seng302.model.stream.parser.RaceStatusData;
import seng302.utilities.Sounds;
/** /**
* Class for storing race data that does not relate to specific vessels or marks such as time or wind. * Class for storing race data that does not relate to specific vessels or marks such as time or wind.
@@ -34,6 +35,7 @@ public class RaceState {
private long serverSystemTime; private long serverSystemTime;
private long expectedStartTime; private long expectedStartTime;
private boolean isRaceStarted = false; private boolean isRaceStarted = false;
private boolean gunFired = false;
long timeTillStart; long timeTillStart;
private ObservableList<ClientYacht> playerPositions; private ObservableList<ClientYacht> playerPositions;
private List<ClientYacht> collisions = new ArrayList<>(); private List<ClientYacht> collisions = new ArrayList<>();
@@ -64,6 +66,10 @@ public class RaceState {
if (raceTime < 0) { if (raceTime < 0) {
return "-" + DATE_TIME_FORMAT.format(-1 * (raceTime - 1000)); return "-" + DATE_TIME_FORMAT.format(-1 * (raceTime - 1000));
} else { } else {
if (!gunFired) {
gunFired = true;
Sounds.playCapGunSound();
}
return DATE_TIME_FORMAT.format(serverSystemTime - expectedStartTime); return DATE_TIME_FORMAT.format(serverSystemTime - expectedStartTime);
} }
} }
@@ -57,7 +57,7 @@ public class RaceStatusData {
* Returns the data for boats collected form race status packets. * Returns the data for boats collected form race status packets.
* *
* @return A list of boat data. Boat data is in the form * @return A list of boat data. Boat data is in the form
* [boatID, estTimeToNextMark, estTimeToFinish, legNumber]. * [boatID, estTimeToNextMark, estTimeToFinish, legNumber, status].
*/ */
public List<long[]> getBoatData () { public List<long[]> getBoatData () {
return boatData; return boatData;
+163
View File
@@ -0,0 +1,163 @@
package seng302.utilities;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
/**
* Static class for playing sounds throughout the program
*
* Created by kre39 on 28/08/17.
*/
public class Sounds {
private static MediaPlayer musicPlayer;
private static MediaPlayer soundEffect;
private static MediaPlayer soundPlayer;
private static boolean musicMuted = false;
private static boolean soundEffectsMuted = false;
public static void stopMusic() {
if (musicPlayer != null) {
musicPlayer.stop();
}
}
public static void setMutes() {
if (soundPlayer != null) {
soundPlayer.setMute(soundEffectsMuted);
}
if (soundEffect != null) {
soundEffect.setMute(soundEffectsMuted);
}
if (musicPlayer != null) {
musicPlayer.setMute(musicMuted);
}
}
public static void stopSoundEffects() {
if (soundEffect != null) {
soundEffect.stop();
}
}
public static void toggleMuteMusic() {
musicMuted = !musicMuted;
if (musicPlayer != null) {
musicPlayer.setMute(musicMuted);
}
}
public static void toggleMuteEffects() {
soundEffectsMuted = !soundEffectsMuted;
if (soundPlayer != null) {
soundPlayer.setMute(soundEffectsMuted);
}
if (soundEffect != null) {
soundEffect.setMute(soundEffectsMuted);
}
}
public static boolean isMusicMuted() {
return musicMuted;
}
public static boolean isSoundEffectsMuted() {
return soundEffectsMuted;
}
public static void playRaceMusic() {
// Media menuMusic = new Media(Sounds.class.getClassLoader().getResource("sounds/Chill-house-music-loop-116-bpm.wav").toString());
Media raceMusic = new Media(Sounds.class.getClassLoader().getResource("sounds/Music-loop-120-bpm.mp3").toString());
musicPlayer = new MediaPlayer(raceMusic);
musicPlayer.setCycleCount(MediaPlayer.INDEFINITE);
musicPlayer.play();
raceMusic = new Media(Sounds.class.getClassLoader().getResource("sounds/Sounds-of-the-ocean.mp3").toString());
soundEffect = new MediaPlayer(raceMusic);
soundEffect.setCycleCount(MediaPlayer.INDEFINITE);
// soundEffect.setVolume(0.3);
soundEffect.play();
musicPlayer.setMute(musicMuted);
soundEffect.setMute(soundEffectsMuted);
}
public static void playMenuMusic() {
Media menuMusic = new Media(
Sounds.class.getClassLoader().getResource("sounds/Elevator-music.mp3").toString());
musicPlayer = new MediaPlayer(menuMusic);
musicPlayer.setCycleCount(MediaPlayer.INDEFINITE);
musicPlayer.play();
}
public static void playFinishMusic() {
Media finishMusic = new Media(Sounds.class.getClassLoader().getResource("sounds/Happy-birthday-song.mp3").toString());
musicPlayer = new MediaPlayer(finishMusic);
musicPlayer.setCycleCount(MediaPlayer.INDEFINITE);
musicPlayer.play();
musicPlayer.setMute(musicMuted);
}
public static void playButtonClick() {
if (!soundEffectsMuted) {
Media buttonClick = new Media(
Sounds.class.getClassLoader().getResource("sounds/Button-click-sound.mp3")
.toString());
soundPlayer = new MediaPlayer(buttonClick);
soundPlayer.play();
soundPlayer.setMute(soundEffectsMuted);
}
}
public static void playFinishSound() {
if (!soundEffectsMuted) {
Media finishSound = new Media(
Sounds.class.getClassLoader().getResource("sounds/Sms-notification.mp3")
.toString());
soundPlayer = new MediaPlayer(finishSound);
soundPlayer.play();
}
}
public static void playMarkRoundingSound() {
if (!soundEffectsMuted) {
Media markRoundingSound = new Media(
Sounds.class.getClassLoader().getResource("sounds/sms-tone.mp3").toString());
soundPlayer = new MediaPlayer(markRoundingSound);
soundPlayer.play();
}
}
public static void playCapGunSound() {
if (!soundEffectsMuted) {
Media gunSound = new Media(
Sounds.class.getClassLoader().getResource("sounds/Gunshot-sound.mp3").toString());
soundPlayer = new MediaPlayer(gunSound);
soundPlayer.play();
}
}
public static void playCrashSound() {
if (!soundEffectsMuted) {
Media crashSound = new Media(
Sounds.class.getClassLoader().getResource("sounds/Large-metal-door-slam.mp3")
.toString());
soundPlayer = new MediaPlayer(crashSound);
soundPlayer.play();
}
}
public static void playHoverSound() {
if (!soundEffectsMuted) {
Media hoverSound = new Media(Sounds.class.getClassLoader().getResource("sounds/sound-over.wav").toString());
soundPlayer = new MediaPlayer(hoverSound);
soundPlayer.play();
}
}
}
@@ -2,9 +2,11 @@ package seng302.utilities;
import java.io.IOException; import java.io.IOException;
import java.io.StringReader; import java.io.StringReader;
import java.lang.reflect.Array;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import javafx.util.Pair;
import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.ParserConfigurationException;
@@ -62,31 +64,10 @@ public class StreamParser {
long windDir = bytesToLong(Arrays.copyOfRange(payload, 18, 20)); long windDir = bytesToLong(Arrays.copyOfRange(payload, 18, 20));
long rawWindSpeed = bytesToLong(Arrays.copyOfRange(payload, 20, 22)); long rawWindSpeed = bytesToLong(Arrays.copyOfRange(payload, 20, 22));
// DateFormat format = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
// currentTime = format.format((new Date(currentTime)))
RaceStatusData data = new RaceStatusData( RaceStatusData data = new RaceStatusData(
windDir, rawWindSpeed, raceStatus, currentTime, expectedStartTime windDir, rawWindSpeed, raceStatus, currentTime, expectedStartTime
); );
// long timeTillStart =
// ((new Date(expectedStartTime)).getTime() - (new Date(currentTime)).getTime()) / 1000;
//
// if (timeTillStart > 0) {
// timeSinceStart = timeTillStart;
// } else {
// if (raceStatus == 4 || raceStatus == 8) {
// raceFinished = true;
// raceStarted = false;
// } else if (!raceStarted) {
// raceStarted = true;
// raceFinished = false;
// }
// timeSinceStart = timeTillStart;
// }
//
//
int noBoats = payload[22]; int noBoats = payload[22];
int raceType = payload[23]; int raceType = payload[23];
long boatID, estTimeAtNextMark, estTimeAtFinish; long boatID, estTimeAtNextMark, estTimeAtFinish;
@@ -106,24 +87,6 @@ public class StreamParser {
return data; return data;
} }
// private static void setBoatLegPosition(Yacht updatingBoat, Integer leg){
// Integer placing = 1;
// if (leg != updatingBoat.getLegNumber() && (raceStarted || raceFinished)) {
// for (Yacht boat : boats.values()) {
// if (boat.getLegNumber() != null && leg <= boat.getLegNumber()){
// placing += 1;
// }
// }
// updatingBoat.setPlacing(placing.toString());
// updatingBoat.setLegNumber(leg);
// boatsPos.putIfAbsent(placing, updatingBoat);
// boatsPos.replace(placing, updatingBoat);
// } else if(updatingBoat.getLegNumber() == null){
// updatingBoat.setPlacing("1");
// updatingBoat.setLegNumber(leg);
// }
// }
/** /**
* Parses and returns the text from a StreamPacket containing text data for display. * Parses and returns the text from a StreamPacket containing text data for display.
* *
@@ -255,15 +218,15 @@ public class StreamParser {
* @return Chatter text message as a string. Returns null if the packet is not of type * @return Chatter text message as a string. Returns null if the packet is not of type
* CHATTER_TEXT. * CHATTER_TEXT.
*/ */
public static String extractChatterText(StreamPacket packet) { public static Pair<Integer, String> extractChatterText(StreamPacket packet) {
if (packet.getType() != PacketType.CHATTER_TEXT) { if (packet.getType() != PacketType.CHATTER_TEXT) {
return null; return null;
} }
byte[] payload = packet.getPayload(); byte[] payload = packet.getPayload();
int messageVersionNo = payload[0]; int messageVersionNo = payload[0];
int messageType = payload[1]; int messageType = payload[1];
int length = payload[2]; int length = (int) bytesToLong(new byte[]{payload[2]});
return new String(Arrays.copyOfRange(payload, 3, 3 + length)); return new Pair<>(messageType, new String(Arrays.copyOfRange(payload, 3, 3 + length)));
} }
/** /**
@@ -392,26 +355,6 @@ public class StreamParser {
}; };
} }
public static void extractBoatAction(StreamPacket packet) {
byte[] payload = packet.getPayload();
int messageVersionNo = payload[0];
long actionType = bytesToLong(Arrays.copyOfRange(payload, 0, 1));
if (actionType == 1) {
System.out.println("VMG");
} else if (actionType == 2) {
System.out.println("SAILS IN");
} else if (actionType == 3) {
System.out.println("SAILS OUT");
} else if (actionType == 4) {
System.out.println("TACK/GYBE");
} else if (actionType == 5) {
System.out.println("UPWIND");
} else if (actionType == 6) {
System.out.println("DOWNWIND");
}
}
/** /**
* takes an array of up to 7 bytes and returns a positive long constructed from the input bytes * takes an array of up to 7 bytes and returns a positive long constructed from the input bytes
* *
@@ -18,6 +18,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import seng302.gameServer.messages.BoatAction; import seng302.gameServer.messages.BoatAction;
import seng302.gameServer.messages.BoatActionMessage; import seng302.gameServer.messages.BoatActionMessage;
import seng302.gameServer.messages.ChatterMessage;
import seng302.gameServer.messages.ClientType; import seng302.gameServer.messages.ClientType;
import seng302.gameServer.messages.CustomizeRequestMessage; import seng302.gameServer.messages.CustomizeRequestMessage;
import seng302.gameServer.messages.CustomizeRequestType; import seng302.gameServer.messages.CustomizeRequestType;
@@ -281,9 +282,17 @@ public class ClientToServerThread implements Runnable {
* @param message The given message type. * @param message The given message type.
*/ */
private void sendBoatActionMessage(BoatActionMessage message) { private void sendBoatActionMessage(BoatActionMessage message) {
sendByteBuffer(message.getBuffer());
}
public void sendChatterMessage(String message) {
sendByteBuffer(new ChatterMessage(clientId, message).getBuffer());
}
private void sendByteBuffer(byte[] bytes) {
if (clientId != -1) { if (clientId != -1) {
try { try {
os.write(message.getBuffer()); os.write(bytes);
} catch (IOException e) { } catch (IOException e) {
logger.warn("IOException on attempting to sendBoatAction from Client"); logger.warn("IOException on attempting to sendBoatAction from Client");
notifyDisconnectListeners("Cannot communicate with server"); notifyDisconnectListeners("Cannot communicate with server");
@@ -292,7 +301,7 @@ public class ClientToServerThread implements Runnable {
} }
} }
private void closeSocket() { public void closeSocket() {
try { try {
socket.close(); socket.close();
socketOpen = false; socketOpen = false;
@@ -1,8 +1,11 @@
package seng302.visualiser; package seng302.visualiser;
import java.io.IOException; import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.ZoneOffset; import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Date;
import java.util.Map; import java.util.Map;
import java.util.TimeZone; import java.util.TimeZone;
import javafx.application.Platform; import javafx.application.Platform;
@@ -12,8 +15,10 @@ import javafx.fxml.FXMLLoader;
import javafx.scene.Node; import javafx.scene.Node;
import javafx.scene.control.Alert; import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType; import javafx.scene.control.Alert.AlertType;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent; import javafx.scene.input.KeyEvent;
import javafx.scene.layout.Pane; import javafx.scene.layout.Pane;
import javafx.util.Pair;
import seng302.gameServer.GameState; import seng302.gameServer.GameState;
import seng302.gameServer.MainServerThread; import seng302.gameServer.MainServerThread;
import seng302.gameServer.messages.BoatAction; import seng302.gameServer.messages.BoatAction;
@@ -28,6 +33,7 @@ import seng302.model.stream.parser.RaceStatusData;
import seng302.model.stream.parser.YachtEventData; import seng302.model.stream.parser.YachtEventData;
import seng302.model.stream.xml.parser.RaceXMLData; import seng302.model.stream.xml.parser.RaceXMLData;
import seng302.model.stream.xml.parser.RegattaXMLData; import seng302.model.stream.xml.parser.RegattaXMLData;
import seng302.utilities.Sounds;
import seng302.utilities.StreamParser; import seng302.utilities.StreamParser;
import seng302.utilities.XMLParser; import seng302.utilities.XMLParser;
import seng302.visualiser.controllers.FinishScreenViewController; import seng302.visualiser.controllers.FinishScreenViewController;
@@ -53,6 +59,8 @@ public class GameClient {
private RaceState raceState = new RaceState(); private RaceState raceState = new RaceState();
private LobbyController lobbyController; private LobbyController lobbyController;
private ArrayList<ClientYacht> finishedBoats = new ArrayList<>();
private ObservableList<String> clientLobbyList = FXCollections.observableArrayList(); private ObservableList<String> clientLobbyList = FXCollections.observableArrayList();
/** /**
@@ -74,7 +82,6 @@ public class GameClient {
startClientToServerThread(ipAddress, portNumber); startClientToServerThread(ipAddress, portNumber);
socketThread.addDisconnectionListener((cause) -> { socketThread.addDisconnectionListener((cause) -> {
showConnectionError(cause); showConnectionError(cause);
tearDownConnection();
Platform.runLater(this::loadStartScreen); Platform.runLater(this::loadStartScreen);
}); });
socketThread.addStreamObserver(this::parsePackets); socketThread.addStreamObserver(this::parsePackets);
@@ -92,10 +99,7 @@ public class GameClient {
lobbyController.setCourseName(""); lobbyController.setCourseName("");
} }
lobbyController.addCloseListener((exitCause) -> { lobbyController.addCloseListener((exitCause) -> this.loadStartScreen());
this.tearDownConnection();
this.loadStartScreen();
});
this.lobbyController = lobbyController; this.lobbyController = lobbyController;
} catch (IOException ioe) { } catch (IOException ioe) {
showConnectionError("Unable to find server"); showConnectionError("Unable to find server");
@@ -113,7 +117,6 @@ public class GameClient {
try { try {
startClientToServerThread(ipAddress, portNumber); startClientToServerThread(ipAddress, portNumber);
socketThread.addDisconnectionListener((cause) -> { socketThread.addDisconnectionListener((cause) -> {
this.tearDownConnection();
Platform.runLater(this::loadStartScreen); Platform.runLater(this::loadStartScreen);
}); });
LobbyController lobbyController = loadLobby(); LobbyController lobbyController = loadLobby();
@@ -134,7 +137,8 @@ public class GameClient {
lobbyController.disableReadyButton(); lobbyController.disableReadyButton();
server.startGame(); server.startGame();
} else if (exitCause == CloseStatus.LEAVE) { } else if (exitCause == CloseStatus.LEAVE) {
tearDownConnection(); server.terminate();
server = null;
loadStartScreen(); loadStartScreen();
} }
}); });
@@ -145,20 +149,11 @@ public class GameClient {
} }
} }
private void tearDownConnection() { private void loadStartScreen() {
if (socketThread != null) {
socketThread.setSocketToClose(); socketThread.setSocketToClose();
if (server != null) {
server.terminate();
server = null;
}
} }
private void loadStartScreen() {
// socketThread.setSocketToClose();
// if (server != null) {
// server.terminate();
// server = null;
// }
FXMLLoader fxmlLoader = new FXMLLoader( FXMLLoader fxmlLoader = new FXMLLoader(
getClass().getResource("/views/StartScreenView.fxml")); getClass().getResource("/views/StartScreenView.fxml"));
try { try {
@@ -207,9 +202,19 @@ public class GameClient {
raceView = fxmlLoader.getController(); raceView = fxmlLoader.getController();
ClientYacht player = allBoatsMap.get(socketThread.getClientId()); ClientYacht player = allBoatsMap.get(socketThread.getClientId());
raceView.loadRace(allBoatsMap, courseData, raceState, player); raceView.loadRace(allBoatsMap, courseData, raceState, player);
raceView.getSendPressedProperty().addListener((obs, old, isPressed) -> {
if (isPressed) {
formatAndSendChatMessage(raceView.readChatInput());
}
});
} }
private void loadFinishScreenView() { private void loadFinishScreenView() {
Sounds.stopMusic();
Sounds.stopSoundEffects();
Sounds.playFinishMusic();
FXMLLoader fxmlLoader = loadFXMLToHolder("/views/FinishScreenView.fxml"); FXMLLoader fxmlLoader = loadFXMLToHolder("/views/FinishScreenView.fxml");
FinishScreenViewController controller = fxmlLoader.getController(); FinishScreenViewController controller = fxmlLoader.getController();
controller.setFinishers(raceState.getPlayerPositions()); controller.setFinishers(raceState.getPlayerPositions());
@@ -293,6 +298,14 @@ public class GameClient {
case YACHT_EVENT_CODE: case YACHT_EVENT_CODE:
showCollisionAlert(StreamParser.extractYachtEventCode(packet)); showCollisionAlert(StreamParser.extractYachtEventCode(packet));
break; break;
case CHATTER_TEXT:
Pair<Integer, String> playerIdMessagePair = StreamParser
.extractChatterText(packet);
raceView.updateChatHistory(
allBoatsMap.get(playerIdMessagePair.getKey()).getColour(),
playerIdMessagePair.getValue()
);
} }
} }
} }
@@ -347,6 +360,9 @@ public class GameClient {
for (ClientYacht yacht : allBoatsMap.values()) { for (ClientYacht yacht : allBoatsMap.values()) {
if (yacht.getBoatStatus() != BoatStatus.FINISHED.getCode()) { if (yacht.getBoatStatus() != BoatStatus.FINISHED.getCode()) {
raceFinished = false; raceFinished = false;
} else if (!finishedBoats.contains(yacht)) {
finishedBoats.add(yacht);
Sounds.playFinishSound();
} }
} }
@@ -362,6 +378,7 @@ public class GameClient {
} }
if (raceFinished) { if (raceFinished) {
Sounds.playFinishSound();
close(); close();
loadFinishScreenView(); loadFinishScreenView();
} }
@@ -385,6 +402,12 @@ public class GameClient {
* @param e The key event triggering this call * @param e The key event triggering this call
*/ */
private void keyPressed(KeyEvent e) { private void keyPressed(KeyEvent e) {
if (raceView.isChatInputFocused()) {
if (e.getCode() == KeyCode.ENTER) {
formatAndSendChatMessage(raceView.readChatInput());
}
return;
}
switch (e.getCode()) { switch (e.getCode()) {
case SPACE: // align with vmg case SPACE: // align with vmg
socketThread.sendBoatAction(BoatAction.VMG); break; socketThread.sendBoatAction(BoatAction.VMG); break;
@@ -393,12 +416,16 @@ public class GameClient {
case PAGE_DOWN: // downwind case PAGE_DOWN: // downwind
socketThread.sendBoatAction(BoatAction.DOWNWIND); break; socketThread.sendBoatAction(BoatAction.DOWNWIND); break;
case ENTER: // tack/gybe case ENTER: // tack/gybe
// if chat box is active take whatever is in there and send it to server
socketThread.sendBoatAction(BoatAction.TACK_GYBE); break; socketThread.sendBoatAction(BoatAction.TACK_GYBE); break;
} }
} }
private void keyReleased(KeyEvent e) { private void keyReleased(KeyEvent e) {
if (raceView.isChatInputFocused()) {
return;
}
switch (e.getCode()) { switch (e.getCode()) {
//TODO 12/07/17 Determine the sail state and send the appropriate packet (eg. if sails are in, send a sail out packet) //TODO 12/07/17 Determine the sail state and send the appropriate packet (eg. if sails are in, send a sail out packet)
case SHIFT: // sails in/sails out case SHIFT: // sails in/sails out
@@ -421,6 +448,7 @@ public class GameClient {
private void showCollisionAlert(YachtEventData yachtEventData) { private void showCollisionAlert(YachtEventData yachtEventData) {
// 33 is the agreed code to show collision // 33 is the agreed code to show collision
if (yachtEventData.getEventId() == 33) { if (yachtEventData.getEventId() == 33) {
Sounds.playCrashSound();
raceState.storeCollision( raceState.storeCollision(
allBoatsMap.get( allBoatsMap.get(
yachtEventData.getSubjectId().intValue() yachtEventData.getSubjectId().intValue()
@@ -428,4 +456,19 @@ public class GameClient {
); );
} }
} }
private void formatAndSendChatMessage(String rawChat) {
if (rawChat.length() > 0) {
socketThread.sendChatterMessage(
new SimpleDateFormat("[HH:mm:ss] ").format(new Date()) +
allBoatsMap.get(socketThread.getClientId()).getShortName() + ": " + rawChat
);
}
}
public ClientToServerThread getSocketThread() {
return socketThread;
}
} }
+22 -4
View File
@@ -35,6 +35,7 @@ import seng302.model.mark.Corner;
import seng302.model.mark.Mark; import seng302.model.mark.Mark;
import seng302.model.token.Token; import seng302.model.token.Token;
import seng302.utilities.GeoUtility; import seng302.utilities.GeoUtility;
import seng302.utilities.Sounds;
import seng302.visualiser.fxObjects.AnnotationBox; import seng302.visualiser.fxObjects.AnnotationBox;
import seng302.visualiser.fxObjects.BoatObject; import seng302.visualiser.fxObjects.BoatObject;
import seng302.visualiser.fxObjects.CourseBoundary; import seng302.visualiser.fxObjects.CourseBoundary;
@@ -63,6 +64,7 @@ public class GameView extends Pane {
private double metersPerPixelX, metersPerPixelY; private double metersPerPixelX, metersPerPixelY;
final double SCALE_DELTA = 1.1; final double SCALE_DELTA = 1.1;
private boolean isZoom = false;
private Text fpsDisplay = new Text(); private Text fpsDisplay = new Text();
private Polygon raceBorder = new CourseBoundary(); private Polygon raceBorder = new CourseBoundary();
@@ -101,7 +103,7 @@ public class GameView extends Pane {
private void zoomOut() { private void zoomOut() {
scaleFactor = 0.1; scaleFactor = 0.1;
if (this.getScaleX() > 0.5) { if (this.isZoom && this.getScaleX() > 0.5) {
this.setScaleX(this.getScaleX() - scaleFactor); this.setScaleX(this.getScaleX() - scaleFactor);
this.setScaleY(this.getScaleY() - scaleFactor); this.setScaleY(this.getScaleY() - scaleFactor);
} }
@@ -109,7 +111,7 @@ public class GameView extends Pane {
private void zoomIn() { private void zoomIn() {
scaleFactor = 0.10; scaleFactor = 0.10;
if (this.getScaleX() < 2.5) { if (this.isZoom && this.getScaleX() < 2.5) {
this.setScaleX(this.getScaleX() + scaleFactor); this.setScaleX(this.getScaleX() + scaleFactor);
this.setScaleY(this.getScaleY() + scaleFactor); this.setScaleY(this.getScaleY() + scaleFactor);
} }
@@ -143,6 +145,13 @@ public class GameView extends Pane {
gameObjects.add(markers); gameObjects.add(markers);
gameObjects.add(tokens); gameObjects.add(tokens);
initializeTimer(); initializeTimer();
this.sceneProperty().addListener(((observable, oldValue, scene) -> {
if (scene != null) {
setupZoom();
} else {
disableZoom();
}
}));
} }
private void initializeTimer() { private void initializeTimer() {
@@ -462,8 +471,7 @@ public class GameView extends Pane {
/** /**
* Enables zoom. Has to be called after this is added to a scene. * Enables zoom. Has to be called after this is added to a scene.
*/ */
public void enableZoom () { private void setupZoom() {
if (this.getScene() != null) {
this.getScene().addEventHandler(KeyEvent.KEY_PRESSED, (event) -> { this.getScene().addEventHandler(KeyEvent.KEY_PRESSED, (event) -> {
if (event.getCode() == KeyCode.Z) { if (event.getCode() == KeyCode.Z) {
zoomIn(); zoomIn();
@@ -471,8 +479,17 @@ public class GameView extends Pane {
zoomOut(); zoomOut();
} }
}); });
enableZoom();
} }
public void enableZoom() {
isZoom = true;
} }
public void disableZoom() {
isZoom = false;
}
/** /**
* Rescales the race to the size of the window. * Rescales the race to the size of the window.
* *
@@ -809,6 +826,7 @@ public class GameView extends Pane {
//Only show arrows for this and next leg. //Only show arrows for this and next leg.
CompoundMark nextMark = null; CompoundMark nextMark = null;
if (legNumber < course.size() - 1) { if (legNumber < course.size() - 1) {
Sounds.playMarkRoundingSound();
nextMark = course.get(legNumber); nextMark = course.get(legNumber);
for (Mark mark : nextMark.getMarks()) { for (Mark mark : nextMark.getMarks()) {
markerObjects.get(mark).showNextEnterArrow(); markerObjects.get(mark).showNextEnterArrow();
@@ -7,6 +7,7 @@ import javafx.scene.control.TextField;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import javafx.stage.Stage; import javafx.stage.Stage;
import seng302.gameServer.messages.CustomizeRequestType; import seng302.gameServer.messages.CustomizeRequestType;
import seng302.utilities.Sounds;
import seng302.visualiser.ClientToServerThread; import seng302.visualiser.ClientToServerThread;
public class CustomizationController { public class CustomizationController {
@@ -34,7 +35,8 @@ public class CustomizationController {
@FXML @FXML
public void submitCustomization() { public void submitCustomization() {
System.out.println("Attempting to send"); Sounds.playButtonClick();
// System.out.println("Attempting to send");
socketThread.sendCustomizationRequest(CustomizeRequestType.NAME, nameField.getText().getBytes()); socketThread.sendCustomizationRequest(CustomizeRequestType.NAME, nameField.getText().getBytes());
// TODO: 16/08/17 ajm412: Turn colors into byte array. // TODO: 16/08/17 ajm412: Turn colors into byte array.
Color color = boatColorPicker.getValue(); Color color = boatColorPicker.getValue();
@@ -15,10 +15,12 @@ import javafx.fxml.Initializable;
import javafx.scene.control.TableColumn; import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView; import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory; import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane; import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane; import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane; import javafx.scene.layout.Pane;
import seng302.model.ClientYacht; import seng302.model.ClientYacht;
import seng302.utilities.Sounds;
public class FinishScreenViewController implements Initializable { public class FinishScreenViewController implements Initializable {
@@ -85,6 +87,11 @@ public class FinishScreenViewController implements Initializable {
} }
public void switchToStartScreenView() { public void switchToStartScreenView() {
Sounds.playButtonClick();
setContentPane("/views/StartScreenView.fxml"); setContentPane("/views/StartScreenView.fxml");
} }
public void playButtonHoverSound(MouseEvent mouseEvent) {
Sounds.playHoverSound();
}
} }
@@ -16,6 +16,7 @@ import javafx.scene.control.Button;
import javafx.scene.control.TextArea; import javafx.scene.control.TextArea;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import javafx.scene.text.Text; import javafx.scene.text.Text;
import javafx.stage.Stage; import javafx.stage.Stage;
@@ -23,6 +24,7 @@ import seng302.gameServer.GameStages;
import seng302.gameServer.GameState; import seng302.gameServer.GameState;
import seng302.model.Colors; import seng302.model.Colors;
import seng302.model.RaceState; import seng302.model.RaceState;
import seng302.utilities.Sounds;
import seng302.visualiser.ClientToServerThread; import seng302.visualiser.ClientToServerThread;
/** /**
@@ -31,6 +33,10 @@ import seng302.visualiser.ClientToServerThread;
*/ */
public class LobbyController { public class LobbyController {
public void playButtonHoverSound(MouseEvent mouseEvent) {
Sounds.playHoverSound();
}
public enum CloseStatus { public enum CloseStatus {
LEAVE, LEAVE,
READY READY
@@ -153,6 +159,7 @@ public class LobbyController {
@FXML @FXML
public void customize() { public void customize() {
Sounds.playButtonClick();
Parent root; Parent root;
try { try {
FXMLLoader fxmlLoader = new FXMLLoader(LobbyController.class.getResource("/views/customizeView.fxml")); FXMLLoader fxmlLoader = new FXMLLoader(LobbyController.class.getResource("/views/customizeView.fxml"));
@@ -184,6 +191,7 @@ public class LobbyController {
@FXML @FXML
public void leaveLobbyButtonPressed() { public void leaveLobbyButtonPressed() {
Sounds.playButtonClick();
// TODO: 10/07/17 wmu16 - Finish function! // TODO: 10/07/17 wmu16 - Finish function!
GameState.setCurrentStage(GameStages.CANCELLED); GameState.setCurrentStage(GameStages.CANCELLED);
// TODO: 20/07/17 wmu16 - Implement some way of terminating the game // TODO: 20/07/17 wmu16 - Implement some way of terminating the game
@@ -193,6 +201,7 @@ public class LobbyController {
@FXML @FXML
public void readyButtonPressed() { public void readyButtonPressed() {
Sounds.playButtonClick();
GameState.setCurrentStage(GameStages.PRE_RACE); GameState.setCurrentStage(GameStages.PRE_RACE);
// Do countdown logic here // Do countdown logic here
@@ -9,6 +9,7 @@ import java.util.TimerTask;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javafx.animation.Timeline; import javafx.animation.Timeline;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener; import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
@@ -25,6 +26,7 @@ import javafx.scene.control.Button;
import javafx.scene.control.CheckBox; import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox; import javafx.scene.control.ComboBox;
import javafx.scene.control.Slider; import javafx.scene.control.Slider;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane; import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Pane; import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
@@ -42,18 +44,30 @@ import seng302.model.mark.CompoundMark;
import seng302.model.mark.Mark; import seng302.model.mark.Mark;
import seng302.model.stream.xml.parser.RaceXMLData; import seng302.model.stream.xml.parser.RaceXMLData;
import seng302.model.token.Token; import seng302.model.token.Token;
import seng302.utilities.Sounds;
import seng302.visualiser.GameView; import seng302.visualiser.GameView;
import seng302.visualiser.controllers.annotations.Annotation; import seng302.visualiser.controllers.annotations.Annotation;
import seng302.visualiser.controllers.annotations.ImportantAnnotationController; import seng302.visualiser.controllers.annotations.ImportantAnnotationController;
import seng302.visualiser.controllers.annotations.ImportantAnnotationDelegate; import seng302.visualiser.controllers.annotations.ImportantAnnotationDelegate;
import seng302.visualiser.controllers.annotations.ImportantAnnotationsState; import seng302.visualiser.controllers.annotations.ImportantAnnotationsState;
import seng302.visualiser.fxObjects.BoatObject; import seng302.visualiser.fxObjects.BoatObject;
import seng302.visualiser.fxObjects.ChatHistory;
/** /**
* Controller class that manages the display of a race * Controller class that manages the display of a race
*/ */
public class RaceViewController extends Thread implements ImportantAnnotationDelegate { public class RaceViewController extends Thread implements ImportantAnnotationDelegate {
private final int CHAT_LIMIT = 128;
@FXML
private Pane basePane;
@FXML
private Button chatSend;
@FXML
private Pane chatHistoryHolder;
@FXML
private TextField chatInput;
@FXML @FXML
private LineChart<String, Double> raceSparkLine; private LineChart<String, Double> raceSparkLine;
@FXML @FXML
@@ -86,26 +100,51 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
private GameView gameView; private GameView gameView;
private RaceState raceState; private RaceState raceState;
private ChatHistory chatHistory;
private Timeline timerTimeline; private Timeline timerTimeline;
private Timer timer = new Timer(); private Timer timer = new Timer();
private List<Series<String, Double>> sparkLineData = new ArrayList<>(); private List<Series<String, Double>> sparkLineData = new ArrayList<>();
private ImportantAnnotationsState importantAnnotations; private ImportantAnnotationsState importantAnnotations;
private ObservableList<ClientYacht> selectionComboBoxList = FXCollections.observableArrayList();
public void initialize() { public void initialize() {
Sounds.stopMusic();
Sounds.playRaceMusic();
// Load a default important annotation state // Load a default important annotation state
importantAnnotations = new ImportantAnnotationsState(); importantAnnotations = new ImportantAnnotationsState();
//Formatting the y axis of the sparkline //Formatting the y axis of the sparkline
// raceSparkLine.getYAxis().setRotate(180); raceSparkLine.getYAxis().setRotate(180);
// raceSparkLine.getYAxis().setTickLabelRotation(180); raceSparkLine.getYAxis().setTickLabelRotation(180);
// raceSparkLine.getYAxis().setTranslateX(-5); raceSparkLine.getYAxis().setTranslateX(-5);
raceSparkLine.visibleProperty().setValue(false); raceSparkLine.visibleProperty().setValue(false);
raceSparkLine.getYAxis().setAutoRanging(false); raceSparkLine.getYAxis().setAutoRanging(false);
sparklineYAxis.setTickMarkVisible(false); sparklineYAxis.setTickMarkVisible(false);
positionVbox.getStylesheets().add(getClass().getResource("/css/master.css").toString()); positionVbox.getStylesheets().add(getClass().getResource("/css/master.css").toString());
selectAnnotationBtn.setOnAction(event -> loadSelectAnnotationView()); selectAnnotationBtn.setOnAction(event -> loadSelectAnnotationView());
chatInput.lengthProperty().addListener((obs, oldLen, newLen) -> {
if (newLen.intValue() > CHAT_LIMIT) {
chatInput.setText(chatInput.getText().substring(0, CHAT_LIMIT));
}
});
chatHistory = new ChatHistory();
chatHistoryHolder.getChildren().addAll(chatHistory);
chatHistory.prefWidthProperty().bind(
chatHistoryHolder.widthProperty()
);
chatHistory.prefHeightProperty().bind(
chatHistoryHolder.heightProperty()
);
// chatHistory.setFitToWidth(true);
// chatHistory.setFitToHeight(true);
// chatHistory.textProperty().addListener((obs, oldValue, newValue) -> {
// chatHistory.setScrollTop(Double.MAX_VALUE);
// });
contentAnchorPane.setOnMouseClicked((event) ->
contentAnchorPane.requestFocus()
);
} }
public void loadRace ( public void loadRace (
@@ -117,12 +156,6 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
this.markers = raceData.getCompoundMarks(); this.markers = raceData.getCompoundMarks();
this.raceState = raceState; this.raceState = raceState;
initializeUpdateTimer();
initialiseFPSCheckBox();
initialiseAnnotationSlider();
initialiseBoatSelectionComboBox();
initialiseSparkLine();
raceState.getPlayerPositions().addListener((ListChangeListener<ClientYacht>) c -> { raceState.getPlayerPositions().addListener((ListChangeListener<ClientYacht>) c -> {
while (c.next()) { while (c.next()) {
if (c.wasPermutated()) { if (c.wasPermutated()) {
@@ -142,7 +175,6 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
gameView.updateCourse( gameView.updateCourse(
new ArrayList<>(raceData.getCompoundMarks().values()), raceData.getMarkSequence() new ArrayList<>(raceData.getCompoundMarks().values()), raceData.getMarkSequence()
); );
gameView.enableZoom();
gameView.setBoatAsPlayer(player); gameView.setBoatAsPlayer(player);
gameView.startRace(); gameView.startRace();
@@ -157,6 +189,20 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
updateWindDirection(raceState.windDirectionProperty().doubleValue()); updateWindDirection(raceState.windDirectionProperty().doubleValue());
updateWindSpeed(raceState.getWindSpeed()); updateWindSpeed(raceState.getWindSpeed());
gameView.setWindDir(raceState.windDirectionProperty().doubleValue()); gameView.setWindDir(raceState.windDirectionProperty().doubleValue());
chatInput.focusedProperty().addListener((obs, oldValue, newValue) -> {
if (newValue) {
gameView.disableZoom();
} else {
gameView.enableZoom();
}
});
Platform.runLater(() -> {
initializeUpdateTimer();
initialiseFPSCheckBox();
initialiseAnnotationSlider();
initialiseBoatSelectionComboBox();
initialiseSparkLine();
});
} }
/** /**
@@ -307,13 +353,6 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
) )
); );
} }
// XYChart.Series<String, Double> positionData = sparkLineData.get(yacht.getSourceID());
// positionData.getData().add(
// new XYChart.Data<>(
// Integer.toString(legNumber),
// 1.0 + participants.size() - yacht.getPlacing()
// )
// );
} }
@@ -535,10 +574,8 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
* for the combobox to take action upon selection * for the combobox to take action upon selection
*/ */
private void initialiseBoatSelectionComboBox() { private void initialiseBoatSelectionComboBox() {
yachtSelectionComboBox.setItems( selectionComboBoxList.setAll(participants.values());
FXCollections.observableArrayList(participants.values()) yachtSelectionComboBox.setItems(selectionComboBoxList);
);
//Null check is if the listener is fired but nothing selected
yachtSelectionComboBox.valueProperty().addListener((obs, lastSelection, selectedBoat) -> { yachtSelectionComboBox.valueProperty().addListener((obs, lastSelection, selectedBoat) -> {
if (selectedBoat != null) { if (selectedBoat != null) {
gameView.selectBoat(selectedBoat); gameView.selectBoat(selectedBoat);
@@ -625,4 +662,24 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
gameView.updateBorder(raceData.getCourseLimit()); gameView.updateBorder(raceData.getCourseLimit());
gameView.updateTokens(raceData.getTokens()); gameView.updateTokens(raceData.getTokens());
} }
public ReadOnlyBooleanProperty getSendPressedProperty() {
return chatSend.pressedProperty();
}
public boolean isChatInputFocused() {
return chatInput.focusedProperty().getValue();
}
public String readChatInput() {
String chat = chatInput.getText();
chatInput.clear();
basePane.requestFocus();
return chat;
}
public void updateChatHistory(Paint playerColour, String newMessage) {
Platform.runLater(() -> chatHistory.addMessage(playerColour, newMessage));
}
} }
@@ -6,12 +6,16 @@ import java.net.NetworkInterface;
import java.net.URL; import java.net.URL;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.ResourceBundle; import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.Initializable; import javafx.fxml.Initializable;
import javafx.scene.control.TextField; import javafx.scene.control.TextField;
import javafx.scene.control.ToggleButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane; import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane; import javafx.scene.layout.GridPane;
import seng302.gameServer.GameState; import seng302.gameServer.GameState;
import seng302.utilities.Sounds;
import seng302.visualiser.GameClient; import seng302.visualiser.GameClient;
/** /**
@@ -20,114 +24,56 @@ import seng302.visualiser.GameClient;
*/ */
public class StartScreenController implements Initializable { public class StartScreenController implements Initializable {
@FXML
private ToggleButton muteMusicButton;
@FXML
private ToggleButton muteSoundsButton;
@FXML @FXML
private TextField ipTextField; private TextField ipTextField;
@FXML @FXML
private TextField portTextField;
@FXML
private GridPane startScreen2;
@FXML
private AnchorPane holder; private AnchorPane holder;
GameClient gameClient; private GameClient gameClient;
public void initialize(URL url, ResourceBundle resourceBundle) { public void initialize(URL url, ResourceBundle resourceBundle) {
Sounds.stopMusic();
Sounds.stopSoundEffects();
Sounds.playMenuMusic();
if (Sounds.isMusicMuted()) {
muteMusicButton.setText("UnMute Music");
} else {
muteMusicButton.setText("Mute Music");
}
if (Sounds.isSoundEffectsMuted()) {
muteSoundsButton.setText("UnMute Sounds");
} else {
muteSoundsButton.setText("Mute Sounds");
}
Sounds.setMutes();
// gameClient = new GameClient(holder); // gameClient = new GameClient(holder);
} }
//
// /**
// * Loads the fxml content into the parent pane
// * @param jfxUrl
// * @return the controller of the fxml
// */
// private Object setContentPane(String jfxUrl) {
// try {
// AnchorPane contentPane = (AnchorPane) startScreen2.getParent();
// contentPane.getChildren().removeAll();
// contentPane.getChildren().clear();
// contentPane.getStylesheets().add(getClass().getResource("/css/master.css").toString());
// FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(jfxUrl));
// contentPane.getChildren().addAll((Pane) fxmlLoader.load());
//
// return fxmlLoader.getController();
// } catch (IOException e) {
// e.printStackTrace();
// }
// return null;
// }
/** /**
* ATTEMPTS TO: * Creates an instance of GameClient and runs it as a host.
* Sets up a new game state with your IP address as designated as the host.
* Starts a thread to listen for incoming connections.
* Starts a client to server thread and connects to own ip.
* Switches to the lobby screen
*/ */
@FXML @FXML
public void hostButtonPressed() { public void hostButtonPressed() {
// new GameState(getLocalHostIp()); Sounds.playButtonClick();
gameClient = new GameClient(holder); gameClient = new GameClient(holder);
gameClient.runAsHost(getLocalHostIp(), 4942); gameClient.runAsHost(getLocalHostIp(), 4942);
// try {
//// String ipAddress = InetAddress.getLocalHost().getHostAddress();
//// new GameState(ipAddress);
//// new MainServerThread();
//// ClientToServerThread clientToServerThread = new ClientToServerThread("localhost", 4950);
//// controller.setClientToServerThread(clientToServerThread);
// // get the lobby controller so that we can pass the game server thread to it
// new GameState(getLocalHostIp());
// MainServerThread mainServerThread = new MainServerThread();
//// ClientState.setHost(true);
// // host will connect and handshake to itself after setting up the server
// // TODO: 24/07/17 wmu16 - Make port number some static global type constant?
//// ClientToServerThread clientToServerThread = new ClientToServerThread(ClientState.getHostIp(), 4942);
//// ClientState.setConnectedToHost(true);
//// controller.setClientToServerThread(clientToServerThread);
// LobbyController lobbyController = (LobbyController) setContentPane("/views/LobbyView.fxml");
// lobbyController.setMainServerThread(mainServerThread);
// } catch (Exception e) {
// Alert alert = new Alert(AlertType.ERROR);
// alert.setHeaderText("Cannot host");
// alert.setContentText("Oops, failed to host, try to restart.");
// alert.showAndWait();
// e.printStackTrace();
// }
} }
/** /**
* ATTEMPTS TO: * Creates an instance of GameClient and runs it has a client.
* Connect to an ip address and port using the ip and port specified on start screen.
* Starts a Client To Server Thread to maintain connection to host.
* Switch view to lobby view.
*/ */
@FXML @FXML
public void connectButtonPressed() { public void connectButtonPressed() {
// TODO: 10/07/17 wmu16 - Finish function // TODO: 10/07/17 wmu16 - Finish function
Sounds.playButtonClick();
gameClient = new GameClient(holder); gameClient = new GameClient(holder);
gameClient.runAsClient(ipTextField.getText().trim().toLowerCase(), 4942); gameClient.runAsClient(ipTextField.getText().trim().toLowerCase(), 4942);
// try {
// String ipAddress = ipTextField.getText().trim().toLowerCase();
// Integer port = Integer.valueOf(portTextField.getText().trim());
//
//// ClientToServerThread clientToServerThread = new ClientToServerThread(ipAddress, port);
//// ClientState.setHost(false);
//// ClientState.setConnectedToHost(true);
//
//// controller.setClientToServerThread(clientToServerThread);
//// setContentPane("/views/LobbyView.fxml");
// } catch (Exception e) {
// Alert alert = new Alert(AlertType.ERROR);
// alert.setHeaderText("Cannot reach the host");
// alert.setContentText("Please check your host IP address.");
// alert.showAndWait();
// }
} }
// public void setController(Controller controller) {
// this.controller = controller;
// }
/** /**
* Gets the local host ip address and sets this ip to ClientState. * Gets the local host ip address and sets this ip to ClientState.
@@ -162,7 +108,30 @@ public class StartScreenController implements Initializable {
if (ipAddress == null) { if (ipAddress == null) {
System.out.println("[HOST] Cannot obtain local host ip address."); System.out.println("[HOST] Cannot obtain local host ip address.");
} }
// ClientState.setHostIp(ipAddress);
return ipAddress; return ipAddress;
} }
public void toggleMusic(ActionEvent actionEvent) {
Sounds.toggleMuteMusic();
Sounds.playButtonClick();
if (Sounds.isMusicMuted()) {
muteMusicButton.setText("UnMute Music");
} else {
muteMusicButton.setText("Mute Music");
}
}
public void toggleSounds(ActionEvent actionEvent) {
Sounds.toggleMuteEffects();
Sounds.playButtonClick();
if (Sounds.isSoundEffectsMuted()) {
muteSoundsButton.setText("UnMute Sounds");
} else {
muteSoundsButton.setText("Mute Sounds");
}
}
public void playButtonHoverSound(MouseEvent mouseEvent) {
Sounds.playHoverSound();
}
} }
@@ -0,0 +1,75 @@
package seng302.visualiser.fxObjects;
import java.util.Arrays;
import javafx.collections.ListChangeListener;
import javafx.scene.Node;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.Background;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
/**
* Extension of a ScrollPane that contains a TextFlow. Has an addMessage() function to parse and
* display chatter text.
*/
public class ChatHistory extends ScrollPane {
private TextFlow textFlow = new TextFlow();
public ChatHistory() {
this.setContent(textFlow);
this.setFitToWidth(true);
this.setFitToHeight(true);
this.setMaxHeight(Double.MAX_VALUE);
this.setMaxWidth(Double.MAX_VALUE);
this.setVbarPolicy(ScrollBarPolicy.AS_NEEDED);
this.setHbarPolicy(ScrollBarPolicy.NEVER);
this.lookup(".scroll-pane").setStyle("-fx-background: rgba(255, 255, 255, 0.1); -fx-background-color: rgba(255, 255, 255, 0.1);");
this.textFlow.setStyle(
"-fx-background: rgba(255, 255, 255, 0.1); -fx-background-color: rgba(255, 255, 255, 0.1);"
);
//This makes the window auto scroll.
textFlow.getChildren().addListener((ListChangeListener<Node>) c ->
this.setVvalue(1.0)
);
//This just makes it so that the ChatHistory is on focus it passes it off to the parent.
this.parentProperty().addListener((obs, old, parent) ->
this.focusedProperty().addListener((obsVal, oldVal, onFocus) -> {
if (onFocus) {
parent.requestFocus();
}
})
);
}
/**
* Adds a message to chat history. Messages should be either of the form:
* "[HH:MM:ss] player_name: message_text" or
* "SERVER: message_text"
* @param colour The colour of the user sending the message
* @param Text The chatter text message to be displayed
*/
public void addMessage (Paint colour, String Text) {
String[] words = Text.split(":");
if (words[0].trim().equals("SERVER")) {
Text text = new Text(Text + "\n\n");
text.setStyle("-fx-font-weight: bolder");
textFlow.getChildren().add(text);
} else {
Text timePlayer = new Text(
String.join(":", Arrays.copyOfRange(words, 0, 3)) + ":"
);
timePlayer.setStyle("-fx-font-weight: bold");
timePlayer.setFill(colour);
Text message = new Text(
String.join(":", Arrays.copyOfRange(words, 3, words.length)) + "\n\n"
);
message.wrappingWidthProperty().bind(this.widthProperty());
textFlow.getChildren().addAll(timePlayer, message);
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,5 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<?import javafx.geometry.Insets?> <?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?> <?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?> <?import javafx.scene.control.Label?>
@@ -10,6 +15,7 @@
<?import javafx.scene.layout.GridPane?> <?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?> <?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.text.Font?> <?import javafx.scene.text.Font?>
<GridPane fx:id="finishScreenGridPane" maxHeight="837.0" maxWidth="837.0" minHeight="837.0" minWidth="837.0" nodeOrientation="LEFT_TO_RIGHT" prefHeight="837.0" prefWidth="837.0" style="-fx-background-color: #2C2c36;" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.visualiser.controllers.FinishScreenViewController"> <GridPane fx:id="finishScreenGridPane" maxHeight="837.0" maxWidth="837.0" minHeight="837.0" minWidth="837.0" nodeOrientation="LEFT_TO_RIGHT" prefHeight="837.0" prefWidth="837.0" style="-fx-background-color: #2C2c36;" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.visualiser.controllers.FinishScreenViewController">
<columnConstraints> <columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
@@ -42,6 +48,6 @@
<Insets bottom="50.0" /> <Insets bottom="50.0" />
</GridPane.margin> </GridPane.margin>
</TableView> </TableView>
<Button mnemonicParsing="false" onAction="#switchToStartScreenView" styleClass="blue-ui-btn" text="Return to Start Screen" GridPane.halignment="CENTER" GridPane.rowIndex="3" GridPane.valignment="TOP" /> <Button mnemonicParsing="false" onAction="#switchToStartScreenView" onMouseEntered="#playButtonHoverSound" styleClass="blue-ui-btn" text="Return to Start Screen" GridPane.halignment="CENTER" GridPane.rowIndex="3" GridPane.valignment="TOP" />
</children> </children>
</GridPane> </GridPane>
+3 -3
View File
@@ -37,9 +37,9 @@
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints> </rowConstraints>
<children> <children>
<Button fx:id="readyButton" focusTraversable="false" mnemonicParsing="false" onAction="#readyButtonPressed" prefWidth="101.0" text="Ready" GridPane.halignment="CENTER" /> <Button fx:id="readyButton" focusTraversable="false" mnemonicParsing="false" onAction="#readyButtonPressed" onMouseEntered="#playButtonHoverSound" prefWidth="101.0" text="Ready" GridPane.halignment="CENTER" />
<Button focusTraversable="false" mnemonicParsing="false" onAction="#leaveLobbyButtonPressed" text="Leave Lobby" GridPane.columnIndex="2" GridPane.halignment="CENTER" /> <Button focusTraversable="false" mnemonicParsing="false" onAction="#leaveLobbyButtonPressed" onMouseEntered="#playButtonHoverSound" text="Leave Lobby" GridPane.columnIndex="2" GridPane.halignment="CENTER" />
<Button fx:id="customizeButton" focusTraversable="false" mnemonicParsing="false" onAction="#customize" text="Customization" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.valignment="CENTER" /> <Button fx:id="customizeButton" focusTraversable="false" mnemonicParsing="false" onAction="#customize" onMouseEntered="#playButtonHoverSound" text="Customization" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.valignment="CENTER" />
</children> </children>
</GridPane> </GridPane>
<GridPane GridPane.rowIndex="1"> <GridPane GridPane.rowIndex="1">
+26 -35
View File
@@ -6,50 +6,41 @@
<?import javafx.scene.layout.*?> <?import javafx.scene.layout.*?>
<?import javafx.scene.shape.*?> <?import javafx.scene.shape.*?>
<?import javafx.scene.text.*?> <?import javafx.scene.text.*?>
<?import javafx.scene.chart.CategoryAxis?>
<?import javafx.scene.chart.LineChart?>
<?import javafx.scene.chart.NumberAxis?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.CheckBox?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Slider?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.shape.Circle?>
<?import javafx.scene.text.Font?>
<?import javafx.scene.text.Text?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="998.0" prefWidth="1530.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.visualiser.controllers.RaceViewController"> <AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="998.0" prefWidth="1530.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.visualiser.controllers.RaceViewController">
<children> <children>
<AnchorPane layoutX="322.0" layoutY="130.0" prefHeight="998.0" prefWidth="1281.0" <AnchorPane fx:id="basePane" layoutX="322.0" layoutY="130.0" prefHeight="998.0" prefWidth="1281.0" style="-fx-background-color: skyblue;" AnchorPane.bottomAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
style="-fx-background-color: skyblue;" AnchorPane.bottomAnchor="0.0"
AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children> <children>
<GridPane prefHeight="998.0" prefWidth="1281.0" AnchorPane.bottomAnchor="0.0" <GridPane prefHeight="998.0" prefWidth="1281.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0"
AnchorPane.topAnchor="0.0">
<columnConstraints> <columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="630.0" minWidth="10.0" <ColumnConstraints hgrow="SOMETIMES" maxWidth="630.0" minWidth="10.0" prefWidth="68.0" />
prefWidth="68.0"/> <ColumnConstraints hgrow="SOMETIMES" maxWidth="1213.0" minWidth="10.0" prefWidth="1213.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="1213.0" minWidth="10.0"
prefWidth="1213.0"/>
</columnConstraints> </columnConstraints>
<rowConstraints> <rowConstraints>
<RowConstraints maxHeight="489.0" minHeight="1.0" prefHeight="24.0" <RowConstraints maxHeight="489.0" minHeight="1.0" prefHeight="24.0" vgrow="SOMETIMES" />
vgrow="SOMETIMES"/> <RowConstraints maxHeight="997.0" minHeight="10.0" prefHeight="974.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="997.0" minHeight="10.0" prefHeight="974.0"
vgrow="SOMETIMES"/>
</rowConstraints> </rowConstraints>
<children> <children>
<AnchorPane fx:id="contentAnchorPane" prefHeight="200.0" prefWidth="200.0" <AnchorPane fx:id="contentAnchorPane" prefHeight="200.0" prefWidth="200.0" GridPane.columnSpan="2" GridPane.rowSpan="2">
GridPane.columnSpan="2" GridPane.rowSpan="2"/> <children>
<Text fx:id="fpsDisplay" strokeType="OUTSIDE" strokeWidth="0.0" text="60 FPS" <AnchorPane layoutX="799.0" layoutY="702.0" prefHeight="214.0" prefWidth="468.0">
GridPane.halignment="CENTER" GridPane.valignment="CENTER"/> <children>
<VBox prefHeight="214.0" prefWidth="468.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
<Pane fx:id="chatHistoryHolder" maxHeight="9.9999999999E10" maxWidth="1.7976931348623157E308" prefHeight="9.9999999999E10" />
<HBox VBox.vgrow="NEVER">
<children>
<TextField fx:id="chatInput" focusTraversable="false" prefHeight="25.0" HBox.hgrow="ALWAYS" />
<Button fx:id="chatSend" focusTraversable="false" mnemonicParsing="false" text="Send" />
</children>
</HBox>
</children>
</VBox>
</children>
</AnchorPane>
</children>
</AnchorPane>
<Text fx:id="fpsDisplay" strokeType="OUTSIDE" strokeWidth="0.0" text="60 FPS" GridPane.halignment="CENTER" GridPane.valignment="CENTER" />
</children> </children>
</GridPane> </GridPane>
</children> </children>
+24 -5
View File
@@ -20,15 +20,13 @@
<children> <children>
<GridPane fx:id="startScreen2" layoutX="365.0" layoutY="285.0" nodeOrientation="LEFT_TO_RIGHT" prefWidth="800.0" style="-fx-background-color: #2C2c36;"> <GridPane fx:id="startScreen2" layoutX="365.0" layoutY="285.0" nodeOrientation="LEFT_TO_RIGHT" prefWidth="800.0" style="-fx-background-color: #2C2c36;">
<children> <children>
<Label alignment="CENTER" text="Party Parrots at Sea" textFill="WHITE" <Label alignment="CENTER" text="Party Parrots at Sea" textFill="WHITE" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="BOTTOM">
GridPane.columnSpan="2147483647" GridPane.halignment="CENTER" GridPane.rowIndex="1"
GridPane.valignment="BOTTOM">
<font> <font>
<Font size="40.0" /> <Font size="40.0" />
</font> </font>
</Label> </Label>
<Button mnemonicParsing="false" onAction="#hostButtonPressed" prefHeight="25.0" prefWidth="175.0" text="Host" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER" GridPane.rowIndex="2" /> <Button mnemonicParsing="false" onAction="#hostButtonPressed" onMouseEntered="#playButtonHoverSound" prefHeight="25.0" prefWidth="175.0" text="Host" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER" GridPane.rowIndex="2" />
<Button mnemonicParsing="false" onAction="#connectButtonPressed" prefHeight="25.0" prefWidth="147.0" text="Connect" GridPane.columnIndex="1" GridPane.rowIndex="4"> <Button mnemonicParsing="false" onAction="#connectButtonPressed" onMouseEntered="#playButtonHoverSound" prefHeight="25.0" prefWidth="147.0" text="Connect" GridPane.columnIndex="1" GridPane.rowIndex="4">
<GridPane.margin> <GridPane.margin>
<Insets left="5.0" right="5.0" /> <Insets left="5.0" right="5.0" />
</GridPane.margin> </GridPane.margin>
@@ -51,6 +49,27 @@
<Insets left="5.0" right="5.0" /> <Insets left="5.0" right="5.0" />
</GridPane.margin> </GridPane.margin>
</TextField> </TextField>
<GridPane GridPane.columnSpan="2" GridPane.rowIndex="5">
<columnConstraints>
<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>
<children>
<ToggleButton fx:id="muteMusicButton" mnemonicParsing="false" onAction="#toggleMusic" onMouseEntered="#playButtonHoverSound" prefWidth="130.0" text="Mute Music" GridPane.halignment="RIGHT">
<GridPane.margin>
<Insets left="5.0" right="5.0" />
</GridPane.margin>
</ToggleButton>
<ToggleButton fx:id="muteSoundsButton" mnemonicParsing="false" onAction="#toggleSounds" onMouseEntered="#playButtonHoverSound" prefWidth="130.0" text="Mute Sounds" GridPane.columnIndex="1">
<GridPane.margin>
<Insets left="5.0" right="5.0" />
</GridPane.margin>
</ToggleButton>
</children>
</GridPane>
</children> </children>
<columnConstraints> <columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="442.0" /> <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="442.0" />
+5
View File
@@ -0,0 +1,5 @@
Feature: SendChat
Scenario: User send chat to another client
Given There are two games running
When the first client has sent the message "Hello world"
Then the other client should receive the message "Hello world"
@@ -0,0 +1,247 @@
package seng302.gameServer.server;
import java.io.IOException;
import org.junit.Assert;
import org.junit.Test;
import seng302.gameServer.GameState;
import seng302.gameServer.MainServerThread;
import seng302.gameServer.messages.BoatStatus;
import seng302.model.stream.packets.StreamPacket;
import seng302.model.stream.parser.RaceStatusData;
import seng302.utilities.StreamParser;
import seng302.visualiser.ClientToServerThread;
/**
* Created by cir27 on 3/09/17.
*/
public class ChatCommandsTest {
private boolean dcSent = false;
private ClientToServerThread client;
private ClientToServerThread host;
private MainServerThread mst;
@Test
public void sendFinishAsHost () {
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
try {
dcSent = false;
new GameState("localhost");
mst = new MainServerThread();
host = new ClientToServerThread("localhost", 4942);
host.addStreamObserver(() -> {
while (host.getPacketQueue().peek() != null) {
StreamPacket packet = host.getPacketQueue().poll();
switch (packet.getType()) {
case RACE_STATUS:
RaceStatusData rsd = StreamParser.extractRaceStatus(packet);
if (rsd.getBoatData().get(0)[4] == BoatStatus.FINISHED.getCode()) {
mst.terminate();
Assert.assertTrue(dcSent);
}
break;
default:
break;
}
}
});
try {
Thread.sleep(100);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
mst.startGame();
try {
Thread.sleep(100);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
host.sendChatterMessage("[time_prefix] <name_prefix> >finish");
dcSent = true;
try {
Thread.sleep(2000);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
host = null;
client = null;
mst = null;
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
@Test
public void sendSpeedAsHostValid () {
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
new GameState("localhost");
mst = new MainServerThread();
host = null;
try {
host = new ClientToServerThread("localhost", 4942);
} catch (IOException ioe) {
ioe.printStackTrace();
}
try {
Thread.sleep(100);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
mst.startGame();
host.sendChatterMessage("[time_prefix] <name_prefix> >speed 5.0");
try {
Thread.sleep(100);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
Assert.assertEquals(5.0, GameState.getSpeedMultiplier(), 0.00001);
mst.terminate();
try {
Thread.sleep(2000);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
host = null;
client = null;
mst = null;
}
@Test
public void sendSpeedAsHostInvalid () {
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
new GameState("localhost");
mst = new MainServerThread();
host = null;
try {
host = new ClientToServerThread("localhost", 4942);
} catch (IOException ioe) {
ioe.printStackTrace();
}
try {
Thread.sleep(100);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
mst.startGame();
host.sendChatterMessage("[time_prefix] <name_prefix> >speed fdgdgdfg");
try {
Thread.sleep(100);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
mst.terminate();
Assert.assertEquals(1.0, GameState.getSpeedMultiplier(), 0.00001);
try {
Thread.sleep(2000);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
@Test
public void sendCommandAsClient () {
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
mst = new MainServerThread();
try {
host = new ClientToServerThread("localhost", 4942);
try {
Thread.sleep(100);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
client = new ClientToServerThread("localhost", 4942);
} catch (IOException ioe) {
ioe.printStackTrace();
}
try {
Thread.sleep(100);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
mst.startGame();
try {
Thread.sleep(200);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
client.sendChatterMessage("[time_prefix] <name_prefix> >speed 5.0");
try {
Thread.sleep(200);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
Assert.assertEquals(1.0, GameState.getSpeedMultiplier(), 0.00001);
mst.terminate();
host.setSocketToClose();
client.setSocketToClose();
try {
Thread.sleep(2000);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
@Test
public void receiveFinishedAsClient () {
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
new GameState("localhost");
dcSent = false;
mst = new MainServerThread();
host = null;
try {
host = new ClientToServerThread("localhost", 4942);
try {
Thread.sleep(100);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
client = new ClientToServerThread("localhost", 4942);
try {
Thread.sleep(100);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
client.addStreamObserver(() -> {
while (client.getPacketQueue().peek() != null) {
StreamPacket packet = client.getPacketQueue().poll();
switch (packet.getType()) {
case RACE_STATUS:
RaceStatusData rsd = StreamParser.extractRaceStatus(packet);
if (rsd.getBoatData().get(0)[4] == BoatStatus.FINISHED.getCode()) {
mst.terminate();
Assert.assertTrue(dcSent);
}
break;
default:
break;
}
}
});
} catch (IOException ioe) {
ioe.printStackTrace();
}
host.sendChatterMessage("[time_prefix] <name_prefix> >finish");
dcSent = true;
}
}
+12 -8
View File
@@ -2,7 +2,10 @@ package seng302.models;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import javafx.util.Pair;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
@@ -21,6 +24,7 @@ public class YachtTest {
@BeforeClass @BeforeClass
public static void setUp() { public static void setUp() {
new GameState("localhost");
y1 = new ServerYacht("Yacht", 1, "Y1", "Y1", "Yacht 1", "C1"); y1 = new ServerYacht("Yacht", 1, "Y1", "Y1", "Yacht 1", "C1");
gs = new GameState("localhost"); gs = new GameState("localhost");
} }
@@ -52,7 +56,7 @@ public class YachtTest {
Double upwind = PolarTable.getOptimalUpwindVMG(windSpeed).keySet().iterator().next(); Double upwind = PolarTable.getOptimalUpwindVMG(windSpeed).keySet().iterator().next();
Double downwind = PolarTable.getOptimalDownwindVMG(windSpeed).keySet().iterator().next(); Double downwind = PolarTable.getOptimalDownwindVMG(windSpeed).keySet().iterator().next();
HashMap<Double, Double> values = new HashMap<>(); List<Pair<Double, Double>> values = new ArrayList<>();
upwind = (double) Math.floorMod(upwind.longValue() + windDirection.longValue(), 360L); upwind = (double) Math.floorMod(upwind.longValue() + windDirection.longValue(), 360L);
Double upwindRight = upwind; Double upwindRight = upwind;
@@ -61,19 +65,19 @@ public class YachtTest {
Double downwindRight = downwind; Double downwindRight = downwind;
Double downwindLeft = 360 - downwindRight; Double downwindLeft = 360 - downwindRight;
values.put(190d, upwindRight); values.add(new Pair<>(190d, upwindRight));
values.put(170d, upwindLeft); values.add(new Pair<>(170d, upwindLeft));
values.put(10d, downwindLeft); values.add(new Pair<>(10d, downwindLeft));
values.put(350d, downwindRight); values.add(new Pair<>(350d, downwindRight));
for (Double begin : values.keySet()) { for (Pair<Double, Double> beginEndPair : values) {
y1.setHeading(begin); y1.setHeading(beginEndPair.getKey());
y1.turnToVMG(); y1.turnToVMG();
for (int i = 0; i < 200; i++) { for (int i = 0; i < 200; i++) {
y1.runAutoPilot(); y1.runAutoPilot();
} }
y1.disableAutoPilot(); y1.disableAutoPilot();
assertEquals(values.get(begin), y1.getHeading(), 5.0); assertEquals(beginEndPair.getValue(), y1.getHeading(), 5.0);
} }
} }
+59
View File
@@ -0,0 +1,59 @@
package steps;
import cucumber.api.java.en.Given;
import cucumber.api.java.en.Then;
import cucumber.api.java.en.When;
import javafx.util.Pair;
import org.junit.Assert;
import seng302.gameServer.MainServerThread;
import seng302.model.stream.packets.StreamPacket;
import seng302.utilities.StreamParser;
import seng302.visualiser.ClientToServerThread;
/**
* Cucumber test for sending chat messages
* Created by kre39 on 7/08/17.
*/
public class SendChatSteps {
private ClientToServerThread client;
private ClientToServerThread host;
private MainServerThread mst;
@Given("^There are two games running$")
public void the_are_two_games_running() throws Throwable {
mst = new MainServerThread();
host = new ClientToServerThread("localhost", 4942);
try {
Thread.sleep(100);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
client = new ClientToServerThread("localhost", 4942);
try {
Thread.sleep(100);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
mst.startGame();
Thread.sleep(200);
}
@When("^the first client has sent the message \"([^\"]*)\"$")
public void the_user_has_pressed_sends_the_message_in_a_text_box(String arg1) throws Throwable {
client.sendChatterMessage("[time_prefix] <name_prefix> " + arg1);
}
@Then("^the other client should receive the message \"([^\"]*)\"$")
public void the_other_client_should_receive_the_message(String arg1) throws Throwable {
Object[] packets = host.getPacketQueue().toArray();
Pair<Integer, String> message = StreamParser.extractChatterText((StreamPacket) packets[packets.length - 1]);
Assert.assertEquals("[time_prefix] <name_prefix> " + arg1, message.getValue());
mst.terminate();
host.setSocketToClose();
client.setSocketToClose();
}
}
+3
View File
@@ -13,6 +13,7 @@ import seng302.model.ServerYacht;
import seng302.visualiser.ClientToServerThread; import seng302.visualiser.ClientToServerThread;
/** /**
* Cucumber test for toggling sail
* Created by kre39 on 7/08/17. * Created by kre39 on 7/08/17.
*/ */
public class ToggleSailSteps { public class ToggleSailSteps {
@@ -49,5 +50,7 @@ public class ToggleSailSteps {
} else { } else {
Assert.assertFalse(yacht.getSailIn()); Assert.assertFalse(yacht.getSailIn());
} }
mst.terminate();
client.closeSocket();
} }
} }