mirror of
https://github.com/michaelrausch/Party-Parrots-At-Sea.git
synced 2026-05-09 06:18:44 +00:00
Merge remote-tracking branch 'origin/develop' into Story1248_Game_App_Scaling
# Conflicts: # src/main/java/seng302/visualiser/GameView.java # src/main/java/seng302/visualiser/controllers/RaceViewController.java # src/main/resources/views/RaceView.fxml
This commit is contained in:
@@ -68,7 +68,7 @@ public class App extends Application {
|
||||
@Override
|
||||
public void start(Stage primaryStage) throws Exception {
|
||||
Parent root = FXMLLoader.load(getClass().getResource("/views/StartScreenView.fxml"));
|
||||
primaryStage.setTitle("RaceVision");
|
||||
primaryStage.setTitle("Party Parrots at Sea");
|
||||
Scene scene = new Scene(root, 1530, 960);
|
||||
scene.getStylesheets().add(getClass().getResource("/css/master.css").toString());
|
||||
primaryStage.setScene(scene);
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package seng302.gameServer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import javafx.scene.paint.Color;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
@@ -15,12 +17,14 @@ import org.w3c.dom.Document;
|
||||
import org.xml.sax.InputSource;
|
||||
import seng302.gameServer.messages.BoatAction;
|
||||
import seng302.gameServer.messages.BoatStatus;
|
||||
import seng302.gameServer.messages.ChatterMessage;
|
||||
import seng302.gameServer.messages.CustomizeRequestType;
|
||||
import seng302.gameServer.messages.MarkRoundingMessage;
|
||||
import seng302.gameServer.messages.MarkType;
|
||||
import seng302.gameServer.messages.Message;
|
||||
import seng302.gameServer.messages.RoundingBoatStatus;
|
||||
import seng302.gameServer.messages.YachtEventCodeMessage;
|
||||
import seng302.gameServer.messages.YachtEventType;
|
||||
import seng302.model.GeoPoint;
|
||||
import seng302.model.Limit;
|
||||
import seng302.model.Player;
|
||||
@@ -29,6 +33,8 @@ import seng302.model.ServerYacht;
|
||||
import seng302.model.mark.CompoundMark;
|
||||
import seng302.model.mark.Mark;
|
||||
import seng302.model.mark.MarkOrder;
|
||||
import seng302.model.token.Token;
|
||||
import seng302.model.token.TokenType;
|
||||
import seng302.utilities.GeoUtility;
|
||||
import seng302.utilities.XMLParser;
|
||||
|
||||
@@ -41,24 +47,31 @@ public class GameState implements Runnable {
|
||||
|
||||
@FunctionalInterface
|
||||
interface NewMessageListener {
|
||||
|
||||
void notify(Message message);
|
||||
}
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(GameState.class);
|
||||
private static Logger logger = LoggerFactory.getLogger(GameState.class);
|
||||
|
||||
|
||||
static final int WARNING_TIME = 10 * -1000;
|
||||
static final int PREPATORY_TIME = 5 * -1000;
|
||||
private static final int TIME_TILL_START = 10 * 1000;
|
||||
static Integer MAX_PLAYERS = 8;
|
||||
|
||||
private static final Long POWERUP_TIMEOUT_MS = 10_000L;
|
||||
|
||||
private static final Integer STATE_UPDATES_PER_SECOND = 60;
|
||||
public static Integer MAX_PLAYERS = 8;
|
||||
public static Double ROUNDING_DISTANCE = 50d; // TODO: 14/08/17 wmu16 - Look into this value further
|
||||
public static final Double MARK_COLLISION_DISTANCE = 15d;
|
||||
private static Double ROUNDING_DISTANCE = 50d; // TODO: 14/08/17 wmu16 - Look into this value further
|
||||
private static final Double MARK_COLLISION_DISTANCE = 15d;
|
||||
public static final Double YACHT_COLLISION_DISTANCE = 25.0;
|
||||
public static final Double BOUNCE_DISTANCE_MARK = 20.0;
|
||||
private static final Double BOUNCE_DISTANCE_MARK = 20.0;
|
||||
public static final Double BOUNCE_DISTANCE_YACHT = 30.0;
|
||||
public static final Double COLLISION_VELOCITY_PENALTY = 0.3;
|
||||
private static final Double COLLISION_VELOCITY_PENALTY = 0.3;
|
||||
|
||||
private static Long previousUpdateTime;
|
||||
public static Double windDirection;
|
||||
private static Double windSpeed;
|
||||
private static Double speedMultiplier = 1d;
|
||||
|
||||
private static Boolean customizationFlag; // dirty flag to tell if a player has customized their boat.
|
||||
|
||||
@@ -72,35 +85,30 @@ public class GameState implements Runnable {
|
||||
private static Set<Mark> marks;
|
||||
private static List<Limit> courseLimit;
|
||||
|
||||
private static List<NewMessageListener> markListeners;
|
||||
private static List<Token> allTokens;
|
||||
private static List<Token> tokensInPlay;
|
||||
|
||||
private static List<NewMessageListener> newMessageListeners;
|
||||
|
||||
private static Map<Player, String> playerStringMap = new HashMap<>();
|
||||
/*
|
||||
Ideally I would like to make this class an object instantiated by the server and given to
|
||||
it's created threads if necessary. Outside of that I think the dependencies on it
|
||||
(atm only Yacht & GameClient) can be removed from most other classes. The observable list of
|
||||
players could be pulled directly from the server by the GameClient since it instantiates it
|
||||
and it is reasonable for it to pull data. The current setup of publicly available statics is
|
||||
pretty meh IMO because anything can change it making it unreliable and like people did with
|
||||
the old ServerParser class everything that needs shared just gets thrown in the static
|
||||
collections and things become a real mess.
|
||||
*/
|
||||
|
||||
public GameState(String hostIpAddress) {
|
||||
windDirection = 180d;
|
||||
windSpeed = 10000d;
|
||||
this.hostIpAddress = hostIpAddress;
|
||||
yachts = new HashMap<>();
|
||||
tokensInPlay = new ArrayList<>();
|
||||
|
||||
players = new ArrayList<>();
|
||||
GameState.hostIpAddress = hostIpAddress;
|
||||
customizationFlag = false;
|
||||
|
||||
speedMultiplier = 1.0;
|
||||
currentStage = GameStages.LOBBYING;
|
||||
isRaceStarted = false;
|
||||
//set this when game stage changes to prerace
|
||||
previousUpdateTime = System.currentTimeMillis();
|
||||
markOrder = new MarkOrder(); //This could be instantiated at some point with a select map?
|
||||
markListeners = new ArrayList<>();
|
||||
newMessageListeners = new ArrayList<>();
|
||||
allTokens = makeTokens();
|
||||
|
||||
resetStartTime();
|
||||
|
||||
@@ -125,6 +133,21 @@ public class GameState implements Runnable {
|
||||
courseLimit = XMLParser.parseRace(document).getCourseLimit();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make a pre defined set of tokensInPlay. //TODO wmu16 - Should read from some file for each
|
||||
* race ideally
|
||||
*
|
||||
* @return A list of possible tokensInPlay for this race
|
||||
*/
|
||||
private ArrayList<Token> makeTokens() {
|
||||
Token token1 = new Token(TokenType.BOOST, 57.66946, 11.83154);
|
||||
Token token2 = new Token(TokenType.BOOST, 57.66877, 11.83382);
|
||||
Token token3 = new Token(TokenType.BOOST, 57.66914, 11.83965);
|
||||
Token token4 = new Token(TokenType.BOOST, 57.66684, 11.83214);
|
||||
return new ArrayList<>(Arrays.asList(token1, token2, token3, token4));
|
||||
}
|
||||
|
||||
public static String getHostIpAddress() {
|
||||
return hostIpAddress;
|
||||
}
|
||||
@@ -137,6 +160,10 @@ public class GameState implements Runnable {
|
||||
return players;
|
||||
}
|
||||
|
||||
public static List<Token> getTokensInPlay() {
|
||||
return tokensInPlay;
|
||||
}
|
||||
|
||||
public static void addPlayer(Player player) {
|
||||
players.add(player);
|
||||
String playerText = player.getYacht().getSourceId() + " " + player.getYacht().getBoatName()
|
||||
@@ -178,7 +205,7 @@ public class GameState implements Runnable {
|
||||
}
|
||||
|
||||
public static void resetStartTime(){
|
||||
startTime = System.currentTimeMillis() + MainServerThread.TIME_TILL_START;
|
||||
startTime = System.currentTimeMillis() + TIME_TILL_START;
|
||||
}
|
||||
|
||||
public static Double getWindDirection() {
|
||||
@@ -264,7 +291,23 @@ public class GameState implements Runnable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called periodically in this GameState thread to update the GameState values
|
||||
* Randomly select a subset of tokensInPlay from a pre defined superset
|
||||
* Broadasts a new race status message to show this update
|
||||
*/
|
||||
public static void spawnNewToken() {
|
||||
Random random = new Random();
|
||||
tokensInPlay.clear();
|
||||
tokensInPlay.add(allTokens.get(random.nextInt(allTokens.size())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Called periodically in this GameState thread to update the GameState values.
|
||||
* -Updates yachts velocity
|
||||
* -Updates locations
|
||||
* -Checks for collisions
|
||||
* -Checks for progression
|
||||
*
|
||||
* -Also checks things like the end of the race and race start time etc
|
||||
*/
|
||||
public void update() {
|
||||
Boolean raceFinished = true;
|
||||
@@ -276,6 +319,7 @@ public class GameState implements Runnable {
|
||||
}
|
||||
for (ServerYacht yacht : yachts.values()) {
|
||||
updateVelocity(yacht);
|
||||
checkPowerUpTimeout(yacht);
|
||||
yacht.runAutoPilot();
|
||||
yacht.updateLocation(timeInterval);
|
||||
if (yacht.getBoatStatus() != BoatStatus.FINISHED) {
|
||||
@@ -283,8 +327,6 @@ public class GameState implements Runnable {
|
||||
checkForLegProgression(yacht);
|
||||
raceFinished = false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (raceFinished) {
|
||||
@@ -292,6 +334,17 @@ public class GameState implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void checkPowerUpTimeout(ServerYacht yacht) {
|
||||
if (yacht.getPowerUp() != null) {
|
||||
if (System.currentTimeMillis() - yacht.getPowerUpStartTime() > POWERUP_TIMEOUT_MS) {
|
||||
yacht.powerDown();
|
||||
logger.debug("Yacht: " + yacht.getShortName() + " powered down!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if the yacht has crossed the course limit
|
||||
*
|
||||
@@ -312,13 +365,45 @@ public class GameState implements Runnable {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks all tokensInPlay to see if a yacht has picked one up
|
||||
* @return Token which was collided with
|
||||
* @param serverYacht The yacht to check for collision with a token
|
||||
*/
|
||||
private static Token checkTokenPickUp(ServerYacht serverYacht) {
|
||||
for (Token token : tokensInPlay) {
|
||||
Double distance = GeoUtility.getDistance(token, serverYacht.getLocation());
|
||||
if (distance < YACHT_COLLISION_DISTANCE) {
|
||||
return token;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks for collision with other in game objects for the given serverYacht. To be called each
|
||||
* update. If there is a collision, Notifies the server to send the appropriate messages out.
|
||||
* Checks for these items in turn:
|
||||
* - Other yachts
|
||||
* - Marks
|
||||
* - Boundary
|
||||
* - Tokens
|
||||
*
|
||||
* @param serverYacht The server yacht to check collisions with
|
||||
*/
|
||||
public static void checkCollision(ServerYacht serverYacht) {
|
||||
//Yacht Collision
|
||||
ServerYacht collidedYacht = checkYachtCollision(serverYacht);
|
||||
Mark collidedMark = checkMarkCollision(serverYacht);
|
||||
|
||||
if (collidedYacht != null) {
|
||||
GeoPoint originalLocation = serverYacht.getLocation();
|
||||
serverYacht.setLocation(
|
||||
calculateBounceBack(serverYacht, originalLocation, BOUNCE_DISTANCE_YACHT)
|
||||
);
|
||||
System.out.println("DID BOUNCE BACK");
|
||||
serverYacht.setCurrentVelocity(
|
||||
serverYacht.getCurrentVelocity() * COLLISION_VELOCITY_PENALTY
|
||||
);
|
||||
@@ -329,59 +414,83 @@ public class GameState implements Runnable {
|
||||
collidedYacht.getCurrentVelocity() * COLLISION_VELOCITY_PENALTY
|
||||
);
|
||||
notifyMessageListeners(
|
||||
new YachtEventCodeMessage(serverYacht.getSourceId())
|
||||
new YachtEventCodeMessage(serverYacht.getSourceId(), YachtEventType.COLLISION)
|
||||
);
|
||||
} else {
|
||||
Mark collidedMark = checkMarkCollision(serverYacht);
|
||||
if (collidedMark != null) {
|
||||
serverYacht.setLocation(
|
||||
calculateBounceBack(serverYacht, collidedMark, BOUNCE_DISTANCE_MARK)
|
||||
);
|
||||
serverYacht.setCurrentVelocity(
|
||||
serverYacht.getCurrentVelocity() * COLLISION_VELOCITY_PENALTY
|
||||
);
|
||||
notifyMessageListeners(
|
||||
new YachtEventCodeMessage(serverYacht.getSourceId())
|
||||
);
|
||||
}
|
||||
else{
|
||||
if (checkBoundaryCollision(serverYacht)) {
|
||||
serverYacht.setLocation(
|
||||
calculateBounceBack(serverYacht, serverYacht.getLocation(),
|
||||
BOUNCE_DISTANCE_YACHT)
|
||||
);
|
||||
serverYacht.setCurrentVelocity(
|
||||
serverYacht.getCurrentVelocity() * COLLISION_VELOCITY_PENALTY
|
||||
);
|
||||
notifyMessageListeners(
|
||||
new YachtEventCodeMessage(serverYacht.getSourceId())
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Mark Collision
|
||||
else if (collidedMark != null) {
|
||||
serverYacht.setLocation(
|
||||
calculateBounceBack(serverYacht, collidedMark, BOUNCE_DISTANCE_MARK)
|
||||
);
|
||||
|
||||
System.out.println("DID BOUNCE BACK2");
|
||||
serverYacht.setCurrentVelocity(
|
||||
serverYacht.getCurrentVelocity() * COLLISION_VELOCITY_PENALTY
|
||||
);
|
||||
notifyMessageListeners(
|
||||
new YachtEventCodeMessage(serverYacht.getSourceId(), YachtEventType.COLLISION)
|
||||
);
|
||||
}
|
||||
|
||||
//Boundary Collision
|
||||
else if (checkBoundaryCollision(serverYacht)) {
|
||||
serverYacht.setLocation(
|
||||
calculateBounceBack(serverYacht, serverYacht.getLocation(),
|
||||
BOUNCE_DISTANCE_YACHT)
|
||||
);
|
||||
|
||||
System.out.println("DID BOUNCE BACK3");
|
||||
serverYacht.setCurrentVelocity(
|
||||
serverYacht.getCurrentVelocity() * COLLISION_VELOCITY_PENALTY
|
||||
);
|
||||
notifyMessageListeners(
|
||||
new YachtEventCodeMessage(serverYacht.getSourceId(), YachtEventType.COLLISION)
|
||||
);
|
||||
}
|
||||
|
||||
//Token Collision
|
||||
Token collidedToken = checkTokenPickUp(serverYacht);
|
||||
if (collidedToken != null) {
|
||||
tokensInPlay.remove(collidedToken);
|
||||
serverYacht.powerUp(collidedToken.getTokenType());
|
||||
logger.debug("Yacht: " + serverYacht.getShortName() + " got powerup " + collidedToken
|
||||
.getTokenType());
|
||||
notifyMessageListeners(MessageFactory.getRaceXML());
|
||||
notifyMessageListeners(
|
||||
new YachtEventCodeMessage(serverYacht.getSourceId(), YachtEventType.TOKEN));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void updateVelocity(ServerYacht yacht) {
|
||||
Double velocity = yacht.getCurrentVelocity();
|
||||
Double trueWindAngle = Math.abs(windDirection - yacht.getHeading());
|
||||
Double boatSpeedInKnots = PolarTable.getBoatSpeed(getWindSpeedKnots(), trueWindAngle);
|
||||
Double maxBoatSpeed = GeoUtility.knotsToMMS(boatSpeedInKnots);
|
||||
Double maxBoatSpeed = GeoUtility.knotsToMMS(boatSpeedInKnots) * speedMultiplier;
|
||||
if (yacht.getPowerUp() != null) {
|
||||
if (yacht.getPowerUp().equals(TokenType.BOOST)) {
|
||||
maxBoatSpeed *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
Double currentVelocity = yacht.getCurrentVelocity();
|
||||
// TODO: 15/08/17 remove magic numbers from these equations.
|
||||
if (yacht.getSailIn()) {
|
||||
if (velocity < maxBoatSpeed - 500) {
|
||||
if (currentVelocity < maxBoatSpeed - 500) {
|
||||
yacht.changeVelocity(maxBoatSpeed / 100);
|
||||
} else if (velocity > maxBoatSpeed + 500) {
|
||||
yacht.changeVelocity(-velocity / 200);
|
||||
} else if (currentVelocity > maxBoatSpeed + 500) {
|
||||
yacht.changeVelocity(-currentVelocity / 200);
|
||||
} else {
|
||||
yacht.setCurrentVelocity(maxBoatSpeed);
|
||||
}
|
||||
} else {
|
||||
if (velocity > 3000) {
|
||||
yacht.changeVelocity(-velocity / 200);
|
||||
} else if (velocity > 100) {
|
||||
yacht.changeVelocity(-velocity / 50);
|
||||
} else if (velocity <= 100) {
|
||||
if (currentVelocity > 3000) {
|
||||
yacht.changeVelocity(-currentVelocity / 200);
|
||||
} else if (currentVelocity > 100) {
|
||||
yacht.changeVelocity(-currentVelocity / 50);
|
||||
} else if (currentVelocity <= 100) {
|
||||
yacht.setCurrentVelocity(0d);
|
||||
}
|
||||
}
|
||||
@@ -671,8 +780,8 @@ public class GameState implements Runnable {
|
||||
}
|
||||
|
||||
private static void notifyMessageListeners(Message message) {
|
||||
for (NewMessageListener mpl : markListeners) {
|
||||
mpl.notify(message);
|
||||
for (NewMessageListener ml : newMessageListeners) {
|
||||
ml.notify(message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -684,8 +793,37 @@ public class GameState implements Runnable {
|
||||
}
|
||||
|
||||
|
||||
public static void addMarkPassListener(NewMessageListener listener) {
|
||||
markListeners.add(listener);
|
||||
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) {
|
||||
newMessageListeners.add(listener);
|
||||
}
|
||||
|
||||
public static void setCustomizationFlag() {
|
||||
@@ -699,4 +837,17 @@ public class GameState implements Runnable {
|
||||
public static void resetCustomizationFlag() {
|
||||
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.Timer;
|
||||
import java.util.TimerTask;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import seng302.model.Player;
|
||||
import seng302.gameServer.messages.Heartbeat;
|
||||
import seng302.gameServer.messages.Message;
|
||||
@@ -14,6 +16,9 @@ import seng302.gameServer.messages.Message;
|
||||
* cannot be sent to a player
|
||||
*/
|
||||
public class HeartbeatThread implements Runnable {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(HeartbeatThread.class);
|
||||
|
||||
private final int HEARTBEAT_PERIOD = 200;
|
||||
private ClientConnectionDelegate delegate;
|
||||
private Integer seqNum;
|
||||
@@ -44,20 +49,23 @@ public class HeartbeatThread implements Runnable {
|
||||
* The delegate is notified if a player has disconnected
|
||||
*/
|
||||
private void sendHeartbeatToAllPlayers(){
|
||||
Message heartbeat = new Heartbeat(seqNum);
|
||||
for (Player player : GameState.getPlayers()){
|
||||
if (!player.getSocket().isConnected()) {
|
||||
playerLostConnection(player);
|
||||
}
|
||||
|
||||
try {
|
||||
player.getSocket().getOutputStream().write(heartbeat.getBuffer());
|
||||
} catch (IOException e) {
|
||||
playerLostConnection(player);
|
||||
try {
|
||||
Message heartbeat = new Heartbeat(seqNum);
|
||||
for (Player player : GameState.getPlayers()) {
|
||||
if (!player.getSocket().isConnected()) {
|
||||
playerLostConnection(player);
|
||||
}
|
||||
try {
|
||||
player.getSocket().getOutputStream().write(heartbeat.getBuffer());
|
||||
} catch (IOException e) {
|
||||
playerLostConnection(player);
|
||||
}
|
||||
}
|
||||
updateDelegate();
|
||||
seqNum++;
|
||||
} catch (NullPointerException ne) {
|
||||
logger.debug("Socket closed between checking for connection and sending heartbeat");
|
||||
}
|
||||
updateDelegate();
|
||||
seqNum++;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
package seng302.gameServer;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import seng302.gameServer.messages.*;
|
||||
import seng302.model.GeoPoint;
|
||||
import seng302.model.Player;
|
||||
import seng302.model.PolarTable;
|
||||
import seng302.model.ServerYacht;
|
||||
import seng302.model.mark.CompoundMark;
|
||||
import seng302.model.token.Token;
|
||||
import seng302.model.token.TokenType;
|
||||
import seng302.utilities.GeoUtility;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ServerSocket;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
@@ -19,18 +22,14 @@ import java.util.*;
|
||||
*/
|
||||
public class MainServerThread implements Runnable, ClientConnectionDelegate {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(MainServerThread.class);
|
||||
|
||||
private static final int PORT = 4942;
|
||||
private static final Integer CLIENT_UPDATES_PER_SECOND = 60;
|
||||
private static final int LOG_LEVEL = 1;
|
||||
private static final int WARNING_TIME = 10 * -1000;
|
||||
private static final int PREPATORY_TIME = 5 * -1000;
|
||||
public static final int TIME_TILL_START = 10 * 1000;
|
||||
|
||||
private static final int MAX_WIND_SPEED = 12000;
|
||||
private static final int MIN_WIND_SPEED = 8000;
|
||||
|
||||
public static int windSpeed = 1000;
|
||||
|
||||
private boolean terminated;
|
||||
|
||||
private Thread thread;
|
||||
@@ -43,13 +42,15 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate {
|
||||
try {
|
||||
serverSocket = new ServerSocket(PORT);
|
||||
} catch (IOException e) {
|
||||
serverLog("IO error in server thread handler upon trying to make new server socket", 0);
|
||||
logger.trace("IO error in server thread handler upon trying to make new server socket",
|
||||
0);
|
||||
}
|
||||
PolarTable.parsePolarFile(getClass().getResourceAsStream("/config/acc_polars.csv"));
|
||||
GameState.addMarkPassListener(this::broadcastMessage);
|
||||
GameState.addMessageEventListener(this::broadcastMessage);
|
||||
terminated = false;
|
||||
thread = new Thread(this, "MainServer");
|
||||
startUpdatingWind();
|
||||
startSpawningTokens();
|
||||
thread.start();
|
||||
}
|
||||
|
||||
@@ -64,50 +65,57 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate {
|
||||
try {
|
||||
Thread.sleep(1000 / CLIENT_UPDATES_PER_SECOND);
|
||||
} catch (InterruptedException e) {
|
||||
serverLog("Interrupted exception in Main Server Thread thread sleep", 1);
|
||||
logger.trace("Interrupted exception in Main Server Thread thread sleep", 1);
|
||||
}
|
||||
if (GameState.getCurrentStage() == GameStages.LOBBYING && GameState
|
||||
.getCustomizationFlag()) {
|
||||
// TODO: 16/08/17 ajm412: This can probably be done in a nicer way via those fancy functional interfaces.
|
||||
for (ServerToClientThread thread : serverToClientThreads) {
|
||||
thread.sendSetupMessages();
|
||||
}
|
||||
sendSetupMessages();
|
||||
GameState.resetCustomizationFlag();
|
||||
}
|
||||
|
||||
if (GameState.getCurrentStage() == GameStages.PRE_RACE) {
|
||||
updateClients();
|
||||
sendBoatLocations();
|
||||
}
|
||||
|
||||
//RACING
|
||||
if (GameState.getCurrentStage() == GameStages.RACING) {
|
||||
updateClients();
|
||||
sendBoatLocations();
|
||||
}
|
||||
|
||||
//FINISHED
|
||||
else if (GameState.getCurrentStage() == GameStages.FINISHED) {
|
||||
terminate();
|
||||
broadcastMessage(MessageFactory.getRaceStatusMessage());
|
||||
try {
|
||||
Thread.sleep(1000); //Hackish fix to make sure all threads have sent closing RaceStatus
|
||||
terminate();
|
||||
} catch (InterruptedException ie) {
|
||||
logger.trace("Thread interrupted while waiting to terminate clients", 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: 14/07/17 wmu16 - Send out disconnect packet to clients
|
||||
try {
|
||||
for (ServerToClientThread serverToClientThread : serverToClientThreads) {
|
||||
serverToClientThread.terminate();
|
||||
}
|
||||
serverSocket.close();
|
||||
return;
|
||||
} catch (IOException e) {
|
||||
System.out.println("IO error in server thread handler upon closing socket");
|
||||
}
|
||||
}
|
||||
|
||||
public void updateClients() {
|
||||
for (ServerToClientThread serverToClientThread : serverToClientThreads) {
|
||||
serverToClientThread.sendBoatLocationPackets();
|
||||
private void sendBoatLocations() {
|
||||
for (ServerYacht serverYacht : GameState.getYachts().values()) {
|
||||
broadcastMessage(MessageFactory.getBoatLocationMessage(serverYacht));
|
||||
}
|
||||
}
|
||||
|
||||
private void sendSetupMessages() {
|
||||
broadcastMessage(MessageFactory.getRaceXML());
|
||||
broadcastMessage(MessageFactory.getRegattaXML());
|
||||
broadcastMessage(MessageFactory.getBoatXML());
|
||||
}
|
||||
|
||||
private void broadcastMessage(Message message) {
|
||||
for (ServerToClientThread serverToClientThread : serverToClientThreads) {
|
||||
serverToClientThread.sendMessage(message);
|
||||
@@ -143,6 +151,25 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate {
|
||||
GameState.setWindDirection(direction.doubleValue());
|
||||
}
|
||||
|
||||
// TODO: 29/08/17 wmu16 - This should not be in one function (init and a scheduling update)
|
||||
public void startGame() {
|
||||
initialiseBoatPositions();
|
||||
Timer t = new Timer();
|
||||
|
||||
t.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
broadcastMessage(MessageFactory.getRaceStatusMessage());
|
||||
if (GameState.getCurrentStage() == GameStages.PRE_RACE
|
||||
|| GameState.getCurrentStage() == GameStages.LOBBYING) {
|
||||
broadcastMessage(MessageFactory.getRaceStartStatusMessage());
|
||||
}
|
||||
}
|
||||
}, 0, 500);
|
||||
}
|
||||
|
||||
|
||||
// TODO: 29/08/17 wmu16 - This sort of update should be in game state
|
||||
private static void startUpdatingWind(){
|
||||
Timer timer = new Timer();
|
||||
timer.schedule(new TimerTask() {
|
||||
@@ -153,12 +180,18 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate {
|
||||
}, 0, 500);
|
||||
}
|
||||
|
||||
|
||||
static void serverLog(String message, int logLevel) {
|
||||
if (logLevel <= LOG_LEVEL) {
|
||||
System.out.println(
|
||||
"[SERVER " + LocalDateTime.now().toLocalTime().toString() + "] " + message);
|
||||
}
|
||||
/**
|
||||
* Start spawning coins every 60s after the first minute
|
||||
*/
|
||||
private void startSpawningTokens() {
|
||||
Timer timer = new Timer();
|
||||
timer.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
GameState.spawnNewToken();
|
||||
broadcastMessage(MessageFactory.getRaceXML());
|
||||
}
|
||||
}, 0, 60000);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -168,13 +201,12 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate {
|
||||
*/
|
||||
@Override
|
||||
public void clientConnected(ServerToClientThread serverToClientThread) {
|
||||
serverLog("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);
|
||||
serverToClientThread.addConnectionListener(() -> {
|
||||
for (ServerToClientThread thread : serverToClientThreads) {
|
||||
thread.sendSetupMessages();
|
||||
}
|
||||
});
|
||||
serverToClientThread.addConnectionListener(this::sendSetupMessages);
|
||||
serverToClientThread.addDisconnectListener(this::clientDisconnected);
|
||||
}
|
||||
|
||||
@@ -185,86 +217,22 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate {
|
||||
*/
|
||||
@Override
|
||||
public void clientDisconnected(Player player) {
|
||||
// try {
|
||||
// player.getSocket().close();
|
||||
// } catch (Exception e) {
|
||||
// serverLog("Cannot disconnect the socket for the disconnected player.", 0);
|
||||
// }
|
||||
serverLog("Player " + player.getYacht().getSourceId() + "'s socket disconnected", 0);
|
||||
logger.debug("Player " + player.getYacht().getSourceId() + "'s socket disconnected", 0);
|
||||
GameState.removeYacht(player.getYacht().getSourceId());
|
||||
GameState.removePlayer(player);
|
||||
ServerToClientThread closedConnection = null;
|
||||
for (ServerToClientThread serverToClientThread : serverToClientThreads) {
|
||||
if (serverToClientThread.getSocket() == player.getSocket()) {
|
||||
closedConnection = serverToClientThread;
|
||||
} else if (GameState.getCurrentStage() != GameStages.RACING){
|
||||
serverToClientThread.sendSetupMessages();
|
||||
}
|
||||
}
|
||||
|
||||
serverToClientThreads.remove(closedConnection);
|
||||
closedConnection.terminate();
|
||||
}
|
||||
|
||||
public void startGame() {
|
||||
initialiseBoatPositions();
|
||||
Timer t = new Timer();
|
||||
|
||||
t.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
broadcastMessage(makeRaceStatusMessage());
|
||||
if (GameState.getCurrentStage() == GameStages.PRE_RACE || GameState.getCurrentStage() == GameStages.LOBBYING) {
|
||||
broadcastMessage(makeRaceStartMessage());
|
||||
}
|
||||
}
|
||||
}, 0, 500);
|
||||
}
|
||||
|
||||
|
||||
private RaceStartStatusMessage makeRaceStartMessage() {
|
||||
Long raceStartTime = GameState.getStartTime();
|
||||
|
||||
return new RaceStartStatusMessage(1, raceStartTime ,
|
||||
1, RaceStartNotificationType.SET_RACE_START_TIME);
|
||||
}
|
||||
|
||||
private RaceStatusMessage makeRaceStatusMessage() {
|
||||
// variables taken from GameServerThread
|
||||
|
||||
List<BoatSubMessage> boatSubMessages = new ArrayList<>();
|
||||
RaceStatus raceStatus;
|
||||
|
||||
for (Player player : GameState.getPlayers()) {
|
||||
ServerYacht y = player.getYacht();
|
||||
BoatSubMessage m = new BoatSubMessage(y.getSourceId(), y.getBoatStatus(),
|
||||
y.getLegNumber(),
|
||||
0, 0, 1234L,
|
||||
1234L);
|
||||
boatSubMessages.add(m);
|
||||
}
|
||||
|
||||
long timeTillStart = System.currentTimeMillis() - GameState.getStartTime();
|
||||
|
||||
if (GameState.getCurrentStage() == GameStages.LOBBYING) {
|
||||
raceStatus = RaceStatus.PRESTART;
|
||||
} else if (GameState.getCurrentStage() == GameStages.PRE_RACE) {
|
||||
raceStatus = RaceStatus.PRESTART;
|
||||
|
||||
if (timeTillStart > WARNING_TIME) {
|
||||
raceStatus = RaceStatus.WARNING;
|
||||
}
|
||||
|
||||
if (timeTillStart > PREPATORY_TIME) {
|
||||
raceStatus = RaceStatus.PREPARATORY;
|
||||
}
|
||||
} else {
|
||||
raceStatus = RaceStatus.STARTED;
|
||||
sendSetupMessages();
|
||||
}
|
||||
|
||||
return new RaceStatusMessage(1, raceStatus, GameState.getStartTime(),
|
||||
GameState.getWindDirection(),
|
||||
GameState.getWindSpeedMMS().longValue(), GameState.getPlayers().size(),
|
||||
RaceType.MATCH_RACE, 1, boatSubMessages);
|
||||
}
|
||||
|
||||
public void terminate() {
|
||||
@@ -275,10 +243,6 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate {
|
||||
* Initialise boats to specific spaced out geopoints behind starting line.
|
||||
*/
|
||||
private void initialiseBoatPositions() {
|
||||
// Getting the start line compound marks
|
||||
// if (gameClient== null) {
|
||||
// return;
|
||||
// }
|
||||
CompoundMark cm = GameState.getMarkOrder().getMarkOrder().get(0);
|
||||
GeoPoint startMark1 = cm.getSubMark(1);
|
||||
GeoPoint startMark2 = cm.getSubMark(2);
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
package seng302.gameServer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import seng302.gameServer.messages.BoatLocationMessage;
|
||||
import seng302.gameServer.messages.BoatSubMessage;
|
||||
import seng302.gameServer.messages.RaceStartNotificationType;
|
||||
import seng302.gameServer.messages.RaceStartStatusMessage;
|
||||
import seng302.gameServer.messages.RaceStatus;
|
||||
import seng302.gameServer.messages.RaceStatusMessage;
|
||||
import seng302.gameServer.messages.RaceType;
|
||||
import seng302.gameServer.messages.XMLMessage;
|
||||
import seng302.gameServer.messages.XMLMessageSubType;
|
||||
import seng302.model.Player;
|
||||
import seng302.model.ServerYacht;
|
||||
import seng302.model.stream.xml.generator.RaceXMLTemplate;
|
||||
import seng302.model.stream.xml.generator.RegattaXMLTemplate;
|
||||
import seng302.model.token.Token;
|
||||
import seng302.utilities.XMLGenerator;
|
||||
|
||||
/**
|
||||
* A Class for interfacing between the data we have in the GameState to the messages we need to send
|
||||
* through the MainServerThread.
|
||||
*
|
||||
* WARNING DO NOT USE THIS CLASS IF GAMESTATE HAS NOT BEEN INSTANTIATED. (Main Server has not started)
|
||||
* // TODO: 29/08/17 wmu16 - Make GameState non static to fix this ¯\_(ツ)_/¯
|
||||
* Created by wmu16 on 29/08/17.
|
||||
*/
|
||||
|
||||
/*
|
||||
Ideally this class would be created with an instance of the GameState (I tried implementing this for
|
||||
a bit) but it was too difficult to properly make GameState non static without doing some proper
|
||||
re working. To do later.
|
||||
*/
|
||||
public class MessageFactory {
|
||||
|
||||
private static XMLGenerator xmlGenerator = new XMLGenerator();
|
||||
|
||||
|
||||
public static RaceStartStatusMessage getRaceStartStatusMessage() {
|
||||
return new RaceStartStatusMessage(
|
||||
1,
|
||||
GameState.getStartTime(),
|
||||
1,
|
||||
RaceStartNotificationType.SET_RACE_START_TIME);
|
||||
}
|
||||
|
||||
public static RaceStatusMessage getRaceStatusMessage() {
|
||||
// variables taken from GameServerThread
|
||||
|
||||
List<BoatSubMessage> boatSubMessages = new ArrayList<>();
|
||||
RaceStatus raceStatus;
|
||||
|
||||
for (Player player : GameState.getPlayers()) {
|
||||
ServerYacht y = player.getYacht();
|
||||
BoatSubMessage m = new BoatSubMessage(y.getSourceId(), y.getBoatStatus(),
|
||||
y.getLegNumber(),
|
||||
0, 0, 1234L,
|
||||
1234L);
|
||||
boatSubMessages.add(m);
|
||||
}
|
||||
|
||||
long timeTillStart = System.currentTimeMillis() - GameState.getStartTime();
|
||||
|
||||
if (GameState.getCurrentStage() == GameStages.LOBBYING) {
|
||||
raceStatus = RaceStatus.PRESTART;
|
||||
} else if (GameState.getCurrentStage() == GameStages.PRE_RACE) {
|
||||
raceStatus = RaceStatus.PRESTART;
|
||||
|
||||
if (timeTillStart > GameState.WARNING_TIME) {
|
||||
raceStatus = RaceStatus.WARNING;
|
||||
}
|
||||
|
||||
if (timeTillStart > GameState.PREPATORY_TIME) {
|
||||
raceStatus = RaceStatus.PREPARATORY;
|
||||
}
|
||||
} else {
|
||||
raceStatus = RaceStatus.STARTED;
|
||||
}
|
||||
|
||||
return new RaceStatusMessage(1, raceStatus, GameState.getStartTime(),
|
||||
GameState.getWindDirection(),
|
||||
GameState.getWindSpeedMMS().longValue(), GameState.getPlayers().size(),
|
||||
RaceType.MATCH_RACE, 1, boatSubMessages);
|
||||
}
|
||||
|
||||
public static BoatLocationMessage getBoatLocationMessage(ServerYacht yacht) {
|
||||
return new BoatLocationMessage(
|
||||
yacht.getSourceId(),
|
||||
0, // TODO: 29/08/17 wmu16 - Work out what to do with seqNo. Currently not used
|
||||
yacht.getLocation().getLat(),
|
||||
yacht.getLocation().getLng(),
|
||||
yacht.getHeading(),
|
||||
yacht.getCurrentVelocity().longValue());
|
||||
}
|
||||
|
||||
public static XMLMessage getRaceXML() {
|
||||
List<ServerYacht> yachts = new ArrayList<>(GameState.getYachts().values());
|
||||
List<Token> tokens = GameState.getTokensInPlay();
|
||||
RaceXMLTemplate raceXMLTemplate = new RaceXMLTemplate(yachts, tokens);
|
||||
xmlGenerator.setRaceTemplate(raceXMLTemplate);
|
||||
|
||||
XMLMessage raceXMLMessage = new XMLMessage(
|
||||
xmlGenerator.getRaceAsXml(),
|
||||
XMLMessageSubType.RACE,
|
||||
xmlGenerator.getRaceAsXml().length());
|
||||
|
||||
return raceXMLMessage;
|
||||
}
|
||||
|
||||
public static XMLMessage getRegattaXML() {
|
||||
//@TODO calculate lat/lng values
|
||||
xmlGenerator.setRegattaTemplate(
|
||||
new RegattaXMLTemplate(
|
||||
"Party Parrot Test Server", "Bermuda Test Course",
|
||||
57.6679590, 11.8503233)
|
||||
);
|
||||
|
||||
return new XMLMessage(
|
||||
xmlGenerator.getRegattaAsXml(),
|
||||
XMLMessageSubType.REGATTA,
|
||||
xmlGenerator.getRegattaAsXml().length());
|
||||
}
|
||||
|
||||
public static XMLMessage getBoatXML() {
|
||||
List<ServerYacht> yachts = new ArrayList<>(GameState.getYachts().values());
|
||||
List<Token> tokens = GameState.getTokensInPlay();
|
||||
RaceXMLTemplate raceXMLTemplate = new RaceXMLTemplate(yachts, tokens);
|
||||
xmlGenerator.setRaceTemplate(raceXMLTemplate);
|
||||
|
||||
return new XMLMessage(
|
||||
xmlGenerator.getBoatsAsXml(),
|
||||
XMLMessageSubType.BOAT,
|
||||
xmlGenerator.getBoatsAsXml().length());
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package seng302.gameServer;
|
||||
|
||||
import java.util.Arrays;
|
||||
import seng302.gameServer.messages.BoatAction;
|
||||
import seng302.gameServer.messages.ChatterMessage;
|
||||
import seng302.gameServer.messages.ClientType;
|
||||
import seng302.gameServer.messages.CustomizeRequestType;
|
||||
import seng302.gameServer.messages.Message;
|
||||
@@ -28,5 +29,18 @@ public class ServerPacketParser {
|
||||
long type = Message.bytesToLong(Arrays.copyOfRange(payload, 4, 5));
|
||||
return CustomizeRequestType.getRequestType((int) type);
|
||||
}
|
||||
|
||||
public static ChatterMessage extractChatterText(byte[] payload) {
|
||||
return new ChatterMessage(
|
||||
payload[1], new String(Arrays.copyOfRange(payload, 3, payload.length))
|
||||
);
|
||||
}
|
||||
|
||||
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 seng302.gameServer.messages.BoatAction;
|
||||
import seng302.gameServer.messages.BoatLocationMessage;
|
||||
import seng302.gameServer.messages.ChatterMessage;
|
||||
import seng302.gameServer.messages.ClientType;
|
||||
import seng302.gameServer.messages.CustomizeRequestType;
|
||||
import seng302.gameServer.messages.Message;
|
||||
@@ -30,29 +31,13 @@ import seng302.gameServer.messages.RegistrationResponseStatus;
|
||||
import seng302.gameServer.messages.XMLMessage;
|
||||
import seng302.gameServer.messages.XMLMessageSubType;
|
||||
import seng302.gameServer.messages.YachtEventCodeMessage;
|
||||
import seng302.gameServer.messages.YachtEventCodeMessage;
|
||||
import seng302.model.Player;
|
||||
import seng302.model.ServerYacht;
|
||||
import seng302.model.stream.packets.PacketType;
|
||||
import seng302.model.stream.packets.StreamPacket;
|
||||
import seng302.model.stream.xml.generator.Race;
|
||||
import seng302.model.stream.xml.generator.Regatta;
|
||||
import seng302.utilities.XMLGenerator;
|
||||
import seng302.gameServer.messages.BoatAction;
|
||||
import seng302.gameServer.messages.BoatLocationMessage;
|
||||
import seng302.gameServer.messages.ClientType;
|
||||
import seng302.gameServer.messages.Message;
|
||||
import seng302.gameServer.messages.RegistrationResponseMessage;
|
||||
import seng302.gameServer.messages.RegistrationResponseStatus;
|
||||
import seng302.gameServer.messages.XMLMessage;
|
||||
import seng302.gameServer.messages.XMLMessageSubType;
|
||||
import seng302.gameServer.messages.YachtEventCodeMessage;
|
||||
import seng302.model.Player;
|
||||
import seng302.model.ServerYacht;
|
||||
import seng302.model.stream.packets.PacketType;
|
||||
import seng302.model.stream.packets.StreamPacket;
|
||||
import seng302.model.stream.xml.generator.Race;
|
||||
import seng302.model.stream.xml.generator.Regatta;
|
||||
import seng302.model.stream.xml.generator.RaceXMLTemplate;
|
||||
import seng302.model.stream.xml.generator.RegattaXMLTemplate;
|
||||
import seng302.model.token.Token;
|
||||
import seng302.utilities.XMLGenerator;
|
||||
|
||||
/**
|
||||
@@ -60,7 +45,7 @@ import seng302.utilities.XMLGenerator;
|
||||
* its own thread. All server threads created and owned by the server thread handler which can
|
||||
* trigger client updates on its threads Created by wmu16 on 13/07/17.
|
||||
*/
|
||||
public class ServerToClientThread implements Runnable, Observer {
|
||||
public class ServerToClientThread implements Runnable {
|
||||
|
||||
/**
|
||||
* Called to notify listeners when this thread receives a connection correctly.
|
||||
@@ -91,8 +76,9 @@ public class ServerToClientThread implements Runnable, Observer {
|
||||
|
||||
private ClientType clientType;
|
||||
private Boolean isRegistered = false;
|
||||
private Boolean isHost = false;
|
||||
|
||||
private XMLGenerator xml;
|
||||
private XMLGenerator xmlGenerator;
|
||||
|
||||
private List<ConnectionListener> connectionListeners = new ArrayList<>();
|
||||
private DisconnectListener disconnectListener;
|
||||
@@ -144,21 +130,11 @@ public class ServerToClientThread implements Runnable, Observer {
|
||||
"Yacht", sourceId, sourceId.toString(), fName, fName + " " + lName, "NZ"
|
||||
);
|
||||
|
||||
yacht.addObserver(this); // TODO: yacht can notify mark rounding message hyi25 13/8/17
|
||||
player = new Player(socket, yacht);
|
||||
GameState.addYacht(sourceId, yacht);
|
||||
GameState.addPlayer(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Observable o, Object arg) {
|
||||
if (arg != null) {
|
||||
sendMessage((Message) arg);
|
||||
} else {
|
||||
sendSetupMessages();
|
||||
}
|
||||
}
|
||||
|
||||
private void completeRegistration(ClientType clientType) throws IOException {
|
||||
// Fail if not a player
|
||||
if (!clientType.equals(ClientType.PLAYER)){
|
||||
@@ -225,7 +201,12 @@ public class ServerToClientThread implements Runnable, Observer {
|
||||
|
||||
completeRegistration(requestedType);
|
||||
break;
|
||||
|
||||
case CHATTER_TEXT:
|
||||
ChatterMessage chatterMessage = ServerPacketParser
|
||||
.extractChatterText(
|
||||
new StreamPacket(type, payloadLength, timeStamp, payload));
|
||||
GameState.processChatter(chatterMessage, isHost);
|
||||
break;
|
||||
case RACE_CUSTOMIZATION_REQUEST:
|
||||
Long sourceID = Message
|
||||
.bytesToLong(Arrays.copyOfRange(payload, 0, 3));
|
||||
@@ -250,36 +231,6 @@ public class ServerToClientThread implements Runnable, Observer {
|
||||
logger.warn("Closed serverToClientThread" + thread, 1);
|
||||
}
|
||||
|
||||
public void sendSetupMessages() {
|
||||
xml = new XMLGenerator();
|
||||
Race race = new Race();
|
||||
|
||||
for (ServerYacht yacht : GameState.getYachts().values()) {
|
||||
race.addBoat(yacht);
|
||||
}
|
||||
|
||||
//@TODO calculate lat/lng values
|
||||
xml.setRegatta(
|
||||
new Regatta(
|
||||
"Party Parrot Test Server", "Bermuda Test Course",
|
||||
57.6679590, 11.8503233)
|
||||
);
|
||||
xml.setRace(race);
|
||||
|
||||
XMLMessage xmlMessage;
|
||||
xmlMessage = new XMLMessage(xml.getRegattaAsXml(), XMLMessageSubType.REGATTA,
|
||||
xml.getRegattaAsXml().length());
|
||||
sendMessage(xmlMessage);
|
||||
|
||||
xmlMessage = new XMLMessage(xml.getBoatsAsXml(), XMLMessageSubType.BOAT,
|
||||
xml.getBoatsAsXml().length());
|
||||
sendMessage(xmlMessage);
|
||||
|
||||
xmlMessage = new XMLMessage(xml.getRaceAsXml(), XMLMessageSubType.RACE,
|
||||
xml.getRaceAsXml().length());
|
||||
sendMessage(xmlMessage);
|
||||
}
|
||||
|
||||
private void closeSocket() {
|
||||
try {
|
||||
socket.close();
|
||||
@@ -334,23 +285,6 @@ public class ServerToClientThread implements Runnable, Observer {
|
||||
return seqNo;
|
||||
}
|
||||
|
||||
|
||||
public void sendBoatLocationPackets() {
|
||||
ArrayList<ServerYacht> yachts = new ArrayList<>(GameState.getYachts().values());
|
||||
for (ServerYacht yacht : yachts) {
|
||||
BoatLocationMessage boatLocationMessage =
|
||||
new BoatLocationMessage(
|
||||
yacht.getSourceId(),
|
||||
getSeqNo(),
|
||||
yacht.getLocation().getLat(),
|
||||
yacht.getLocation().getLng(),
|
||||
yacht.getHeading(),
|
||||
yacht.getCurrentVelocity().longValue());
|
||||
|
||||
sendMessage(boatLocationMessage);
|
||||
}
|
||||
}
|
||||
|
||||
public Thread getThread() {
|
||||
return thread;
|
||||
}
|
||||
@@ -363,10 +297,6 @@ public class ServerToClientThread implements Runnable, Observer {
|
||||
return yacht;
|
||||
}
|
||||
|
||||
public void sendCollisionMessage(Integer yachtId) {
|
||||
sendMessage(new YachtEventCodeMessage(yachtId));
|
||||
}
|
||||
|
||||
public void addConnectionListener(ConnectionListener listener) {
|
||||
connectionListeners.add(listener);
|
||||
}
|
||||
@@ -386,4 +316,8 @@ public class ServerToClientThread implements Runnable, Observer {
|
||||
public void addDisconnectListener(DisconnectListener disconnectListener) {
|
||||
this.disconnectListener = disconnectListener;
|
||||
}
|
||||
|
||||
public void setAsHost() {
|
||||
isHost = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,9 +11,11 @@ public class ChatterMessage extends Message {
|
||||
private int message_size = 21;
|
||||
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_size = message_size;
|
||||
this.message_size = byteMessage.length;
|
||||
this.message = message;
|
||||
|
||||
setHeader(new Header(MessageType.CHATTER_TEXT, 1, (short) getSize()));
|
||||
@@ -23,7 +25,7 @@ public class ChatterMessage extends Message {
|
||||
putByte((byte) MESSAGE_VERSION_NUMBER);
|
||||
putInt(message_type, 1);
|
||||
putInt(message_size, 1);
|
||||
putBytes(message.getBytes());
|
||||
putBytes(byteMessage);
|
||||
|
||||
writeCRC();
|
||||
rewind();
|
||||
@@ -34,5 +36,11 @@ public class ChatterMessage extends Message {
|
||||
return MESSAGE_SIZE + message_size;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public int getMessage_type() {
|
||||
return message_type;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,13 +18,13 @@ public class YachtEventCodeMessage extends Message {
|
||||
private int eventId;
|
||||
|
||||
|
||||
public YachtEventCodeMessage(Integer subjectId) {
|
||||
public YachtEventCodeMessage(Integer subjectId, YachtEventType yachtEventType) {
|
||||
timeStamp = System.currentTimeMillis() / 1000L;
|
||||
ack = 0;
|
||||
raceId = 1;
|
||||
destSourceId = subjectId; // collision boat source id
|
||||
incidentId = 0;
|
||||
eventId = 33;
|
||||
eventId = yachtEventType.getCode();
|
||||
|
||||
setHeader(new Header(MESSAGE_TYPE, 0x01, (short) getSize()));
|
||||
allocateBuffer();
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package seng302.gameServer.messages;
|
||||
|
||||
/**
|
||||
* Created by wmu16 on 11/09/17.
|
||||
*/
|
||||
public enum YachtEventType {
|
||||
COLLISION(33),
|
||||
TOKEN(34);
|
||||
|
||||
private int code;
|
||||
|
||||
YachtEventType(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,6 @@ import javafx.beans.property.ReadOnlyLongWrapper;
|
||||
import javafx.scene.paint.Color;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import seng302.model.mark.CompoundMark;
|
||||
|
||||
/**
|
||||
* Yacht class for the racing boat. <p> Class created to store more variables (eg. boat statuses)
|
||||
@@ -32,7 +31,7 @@ public class ClientYacht extends Observable {
|
||||
|
||||
@FunctionalInterface
|
||||
public interface MarkRoundingListener {
|
||||
void notifyRounding(ClientYacht yacht, CompoundMark markPassed, int legNumber);
|
||||
void notifyRounding(ClientYacht yacht, int legNumber);
|
||||
}
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(ClientYacht.class);
|
||||
@@ -63,7 +62,6 @@ public class ClientYacht extends Observable {
|
||||
private ReadOnlyLongWrapper timeTillNextProperty = new ReadOnlyLongWrapper();
|
||||
private ReadOnlyLongWrapper timeSinceLastMarkProperty = new ReadOnlyLongWrapper();
|
||||
private ReadOnlyIntegerWrapper placingProperty = new ReadOnlyIntegerWrapper();
|
||||
private CompoundMark lastMarkRounded;
|
||||
private Color colour;
|
||||
|
||||
public ClientYacht(String boatType, Integer sourceId, String hullID, String shortName,
|
||||
@@ -189,14 +187,6 @@ public class ClientYacht extends Observable {
|
||||
return markRoundTime;
|
||||
}
|
||||
|
||||
public CompoundMark getLastMarkRounded() {
|
||||
return lastMarkRounded;
|
||||
}
|
||||
|
||||
public void setLastMarkRounded(CompoundMark lastMarkRounded) {
|
||||
this.lastMarkRounded = lastMarkRounded;
|
||||
}
|
||||
|
||||
public GeoPoint getLocation() {
|
||||
return location;
|
||||
}
|
||||
@@ -286,13 +276,12 @@ public class ClientYacht extends Observable {
|
||||
return sailIn;
|
||||
}
|
||||
|
||||
public void roundMark(CompoundMark mark, long markRoundTime, long timeSinceLastMark) {
|
||||
public void roundMark(long markRoundTime, long timeSinceLastMark) {
|
||||
this.markRoundTime = markRoundTime;
|
||||
timeSinceLastMarkProperty.set(timeSinceLastMark);
|
||||
lastMarkRounded = mark;
|
||||
legNumber++;
|
||||
for (MarkRoundingListener listener : markRoundingListeners) {
|
||||
listener.notifyRounding(this, lastMarkRounded, legNumber);
|
||||
listener.notifyRounding(this, legNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import seng302.model.stream.parser.RaceStartData;
|
||||
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.
|
||||
@@ -34,6 +35,7 @@ public class RaceState {
|
||||
private long serverSystemTime;
|
||||
private long expectedStartTime;
|
||||
private boolean isRaceStarted = false;
|
||||
private boolean gunFired = false;
|
||||
long timeTillStart;
|
||||
private ObservableList<ClientYacht> playerPositions;
|
||||
private List<ClientYacht> collisions = new ArrayList<>();
|
||||
@@ -64,6 +66,10 @@ public class RaceState {
|
||||
if (raceTime < 0) {
|
||||
return "-" + DATE_TIME_FORMAT.format(-1 * (raceTime - 1000));
|
||||
} else {
|
||||
if (!gunFired) {
|
||||
gunFired = true;
|
||||
Sounds.playCapGunSound();
|
||||
}
|
||||
return DATE_TIME_FORMAT.format(serverSystemTime - expectedStartTime);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import org.slf4j.LoggerFactory;
|
||||
import seng302.gameServer.GameState;
|
||||
import seng302.gameServer.messages.BoatStatus;
|
||||
import seng302.model.mark.Mark;
|
||||
import seng302.model.token.TokenType;
|
||||
import seng302.utilities.GeoUtility;
|
||||
|
||||
/**
|
||||
@@ -16,7 +17,7 @@ import seng302.utilities.GeoUtility;
|
||||
* compared to the XMLParser boat class, also done outside Boat class because some old variables are
|
||||
* not used anymore.
|
||||
*/
|
||||
public class ServerYacht extends Observable {
|
||||
public class ServerYacht {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(ClientYacht.class);
|
||||
|
||||
@@ -30,10 +31,8 @@ public class ServerYacht extends Observable {
|
||||
private String boatName;
|
||||
private String country;
|
||||
private BoatStatus boatStatus;
|
||||
|
||||
private Color boatColor;
|
||||
|
||||
|
||||
//Location
|
||||
private Double lastHeading;
|
||||
private Boolean sailIn;
|
||||
@@ -52,6 +51,10 @@ public class ServerYacht extends Observable {
|
||||
private Boolean hasPassedLine;
|
||||
private Boolean hasPassedThroughGate;
|
||||
|
||||
//PowerUp
|
||||
private TokenType powerUp;
|
||||
private Long powerUpStartTime;
|
||||
|
||||
|
||||
public ServerYacht(String boatType, Integer sourceId, String hullID, String shortName,
|
||||
String boatName, String country) {
|
||||
@@ -71,6 +74,7 @@ public class ServerYacht extends Observable {
|
||||
this.currentMarkSeqID = 0;
|
||||
this.legNumber = 0;
|
||||
this.boatColor = Colors.getColor(sourceId - 1);
|
||||
this.powerUp = null;
|
||||
|
||||
this.hasEnteredRoundingZone = false;
|
||||
this.hasPassedLine = false;
|
||||
@@ -101,13 +105,21 @@ public class ServerYacht extends Observable {
|
||||
location = geoPoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add ServerToClientThread as the observer, this observer pattern mainly server for the boat
|
||||
* rounding package.
|
||||
*/
|
||||
@Override
|
||||
public void addObserver(Observer o) {
|
||||
super.addObserver(o);
|
||||
public void powerUp(TokenType powerUp) {
|
||||
this.powerUp = powerUp;
|
||||
powerUpStartTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public void powerDown() {
|
||||
this.powerUp = null;
|
||||
}
|
||||
|
||||
public Long getPowerUpStartTime() {
|
||||
return powerUpStartTime;
|
||||
}
|
||||
|
||||
public TokenType getPowerUp() {
|
||||
return powerUp;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,8 +11,10 @@ import org.w3c.dom.Document;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
import seng302.gameServer.messages.RoundingSide;
|
||||
import seng302.model.stream.xml.generator.Race;
|
||||
import seng302.model.ServerYacht;
|
||||
import seng302.model.stream.xml.generator.RaceXMLTemplate;
|
||||
import seng302.model.stream.xml.parser.RaceXMLData;
|
||||
import seng302.model.token.Token;
|
||||
import seng302.utilities.XMLGenerator;
|
||||
import seng302.utilities.XMLParser;
|
||||
import java.util.*;
|
||||
@@ -125,7 +127,10 @@ public class MarkOrder {
|
||||
private void loadRaceProperties(){
|
||||
XMLGenerator generator = new XMLGenerator();
|
||||
|
||||
generator.setRace(new Race());
|
||||
// TODO: 29/08/17 wmu16 - This is terrible, having to make a template just to receive constant data
|
||||
generator.setRaceTemplate(new RaceXMLTemplate(
|
||||
new ArrayList<>(),
|
||||
new ArrayList<>()));
|
||||
|
||||
String raceXML = generator.getRaceAsXml();
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ public class RaceStatusData {
|
||||
* Returns the data for boats collected form race status packets.
|
||||
*
|
||||
* @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 () {
|
||||
return boatData;
|
||||
|
||||
+15
-11
@@ -5,28 +5,23 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import seng302.model.ServerYacht;
|
||||
import seng302.model.token.Token;
|
||||
|
||||
/**
|
||||
* A Race object that can be parsed into XML
|
||||
*/
|
||||
public class Race {
|
||||
public class RaceXMLTemplate {
|
||||
|
||||
private List<ServerYacht> yachts;
|
||||
private LocalDateTime startTime;
|
||||
private List<Token> tokens;
|
||||
|
||||
public Race(){
|
||||
yachts = new ArrayList<>();
|
||||
public RaceXMLTemplate(List<ServerYacht> yachts, List<Token> tokens) {
|
||||
this.yachts = yachts;
|
||||
this.tokens = tokens;
|
||||
startTime = LocalDateTime.now();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a boat to the race
|
||||
* @param yacht The boat to add
|
||||
*/
|
||||
public void addBoat(ServerYacht yacht) {
|
||||
yachts.add(yacht);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of boats in the race
|
||||
* @return A List of boats
|
||||
@@ -35,6 +30,15 @@ public class Race {
|
||||
return Collections.unmodifiableList(yachts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of tokens in the race
|
||||
*
|
||||
* @return A list of tokens
|
||||
*/
|
||||
public List<Token> getTokens() {
|
||||
return Collections.unmodifiableList(tokens);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the time until the race starts
|
||||
* @param seconds The time in seconds until the race starts
|
||||
+2
-2
@@ -3,7 +3,7 @@ package seng302.model.stream.xml.generator;
|
||||
/**
|
||||
* A Race regatta that can be parsed into XML
|
||||
*/
|
||||
public class Regatta {
|
||||
public class RegattaXMLTemplate {
|
||||
private final Double DEFAULT_ALTITUDE = 0d;
|
||||
private final Integer DEFAULT_REGATTA_ID = 0;
|
||||
|
||||
@@ -18,7 +18,7 @@ public class Regatta {
|
||||
private Integer utcOffset;
|
||||
private Double magneticVariation;
|
||||
|
||||
public Regatta(String name, String courseName, Double latitude, Double longitude) {
|
||||
public RegattaXMLTemplate(String name, String courseName, Double latitude, Double longitude) {
|
||||
this.name = name;
|
||||
this.id = DEFAULT_REGATTA_ID;
|
||||
this.courseName = courseName;
|
||||
@@ -6,6 +6,7 @@ import java.util.Map;
|
||||
import seng302.model.Limit;
|
||||
import seng302.model.mark.CompoundMark;
|
||||
import seng302.model.mark.Corner;
|
||||
import seng302.model.token.Token;
|
||||
|
||||
/**
|
||||
* Process a Document object containing race data in XML format and stores the data.
|
||||
@@ -13,13 +14,16 @@ import seng302.model.mark.Corner;
|
||||
public class RaceXMLData {
|
||||
|
||||
private List<Integer> participants;
|
||||
private List<Token> tokens;
|
||||
private Map<Integer, CompoundMark> compoundMarks;
|
||||
private List<Corner> markSequence;
|
||||
private List<Limit> courseLimit;
|
||||
|
||||
public RaceXMLData(List<Integer> participants, List<CompoundMark> compoundMarks,
|
||||
public RaceXMLData(List<Integer> participants, List<Token> tokens,
|
||||
List<CompoundMark> compoundMarks,
|
||||
List<Corner> markSequence, List<Limit> courseLimit) {
|
||||
this.participants = participants;
|
||||
this.tokens = tokens;
|
||||
this.markSequence = markSequence;
|
||||
this.courseLimit = courseLimit;
|
||||
this.compoundMarks = new HashMap<>();
|
||||
@@ -32,6 +36,10 @@ public class RaceXMLData {
|
||||
return participants;
|
||||
}
|
||||
|
||||
public List<Token> getTokens() {
|
||||
return tokens;
|
||||
}
|
||||
|
||||
public Map<Integer, CompoundMark> getCompoundMarks() {
|
||||
return compoundMarks;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package seng302.model.token;
|
||||
|
||||
import seng302.model.GeoPoint;
|
||||
|
||||
/**
|
||||
* A class describing a game token
|
||||
* Created by wmu16 on 28/08/17.
|
||||
*/
|
||||
public class Token extends GeoPoint {
|
||||
|
||||
private TokenType tokenType;
|
||||
|
||||
public Token(TokenType tokenType, double lat, double lng) {
|
||||
super(lat, lng);
|
||||
this.tokenType = tokenType;
|
||||
}
|
||||
|
||||
public TokenType getTokenType() {
|
||||
return tokenType;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package seng302.model.token;
|
||||
|
||||
/**
|
||||
* An enum describing the different types of game objects
|
||||
* Created by wmu16 on 28/08/17.
|
||||
*/
|
||||
public enum TokenType {
|
||||
BOOST(0),
|
||||
HANDLING(1);
|
||||
|
||||
private int value;
|
||||
|
||||
TokenType(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public static TokenType getToken(int value) {
|
||||
switch (value) {
|
||||
case 0:
|
||||
return BOOST;
|
||||
case 1:
|
||||
return HANDLING;
|
||||
default:
|
||||
return BOOST;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
package seng302.utilities;
|
||||
|
||||
import javafx.scene.media.AudioClip;
|
||||
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 AudioClip hoverSoundPlayer = new AudioClip(Sounds.class.getClassLoader().getResource("sounds/sound-over.wav").toExternalForm());;
|
||||
|
||||
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.setVolume(0.3);
|
||||
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.setVolume(0.3);
|
||||
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.setVolume(0.3);
|
||||
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.setVolume(0.5);
|
||||
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.setVolume(0.5);
|
||||
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 playTokenPickupSound() {
|
||||
if (!soundEffectsMuted) {
|
||||
Media pickupSound = new Media(
|
||||
Sounds.class.getClassLoader().getResource("sounds/Coin-pick-up-sound-effect.mp3")
|
||||
.toString());
|
||||
soundPlayer = new MediaPlayer(pickupSound);
|
||||
soundPlayer.play();
|
||||
}
|
||||
}
|
||||
|
||||
public static void playHoverSound() {
|
||||
if (!soundEffectsMuted) {
|
||||
hoverSoundPlayer.setVolume(2.5);
|
||||
hoverSoundPlayer.play();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -2,9 +2,11 @@ package seng302.utilities;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import javafx.util.Pair;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
@@ -62,31 +64,10 @@ public class StreamParser {
|
||||
long windDir = bytesToLong(Arrays.copyOfRange(payload, 18, 20));
|
||||
long rawWindSpeed = bytesToLong(Arrays.copyOfRange(payload, 20, 22));
|
||||
|
||||
// DateFormat format = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
|
||||
// currentTime = format.format((new Date(currentTime)))
|
||||
|
||||
RaceStatusData data = new RaceStatusData(
|
||||
windDir, rawWindSpeed, raceStatus, currentTime, expectedStartTime
|
||||
);
|
||||
|
||||
// long timeTillStart =
|
||||
// ((new Date(expectedStartTime)).getTime() - (new Date(currentTime)).getTime()) / 1000;
|
||||
//
|
||||
// if (timeTillStart > 0) {
|
||||
// timeSinceStart = timeTillStart;
|
||||
// } else {
|
||||
// if (raceStatus == 4 || raceStatus == 8) {
|
||||
// raceFinished = true;
|
||||
// raceStarted = false;
|
||||
// } else if (!raceStarted) {
|
||||
// raceStarted = true;
|
||||
// raceFinished = false;
|
||||
// }
|
||||
// timeSinceStart = timeTillStart;
|
||||
// }
|
||||
//
|
||||
|
||||
//
|
||||
int noBoats = payload[22];
|
||||
int raceType = payload[23];
|
||||
long boatID, estTimeAtNextMark, estTimeAtFinish;
|
||||
@@ -106,24 +87,6 @@ public class StreamParser {
|
||||
return data;
|
||||
}
|
||||
|
||||
// private static void setBoatLegPosition(Yacht updatingBoat, Integer leg){
|
||||
// Integer placing = 1;
|
||||
// if (leg != updatingBoat.getLegNumber() && (raceStarted || raceFinished)) {
|
||||
// for (Yacht boat : boats.values()) {
|
||||
// if (boat.getLegNumber() != null && leg <= boat.getLegNumber()){
|
||||
// placing += 1;
|
||||
// }
|
||||
// }
|
||||
// updatingBoat.setPlacing(placing.toString());
|
||||
// updatingBoat.setLegNumber(leg);
|
||||
// boatsPos.putIfAbsent(placing, updatingBoat);
|
||||
// boatsPos.replace(placing, updatingBoat);
|
||||
// } else if(updatingBoat.getLegNumber() == null){
|
||||
// updatingBoat.setPlacing("1");
|
||||
// updatingBoat.setLegNumber(leg);
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* Parses and returns the text from a StreamPacket containing text data for display.
|
||||
*
|
||||
@@ -255,15 +218,15 @@ public class StreamParser {
|
||||
* @return Chatter text message as a string. Returns null if the packet is not of type
|
||||
* CHATTER_TEXT.
|
||||
*/
|
||||
public static String extractChatterText(StreamPacket packet) {
|
||||
public static Pair<Integer, String> extractChatterText(StreamPacket packet) {
|
||||
if (packet.getType() != PacketType.CHATTER_TEXT) {
|
||||
return null;
|
||||
}
|
||||
byte[] payload = packet.getPayload();
|
||||
int messageVersionNo = payload[0];
|
||||
int messageType = payload[1];
|
||||
int length = payload[2];
|
||||
return new String(Arrays.copyOfRange(payload, 3, 3 + length));
|
||||
int length = (int) bytesToLong(new byte[]{payload[2]});
|
||||
return new Pair<>(messageType, new String(Arrays.copyOfRange(payload, 3, 3 + length)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -392,26 +355,6 @@ public class StreamParser {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
public static void extractBoatAction(StreamPacket packet) {
|
||||
byte[] payload = packet.getPayload();
|
||||
int messageVersionNo = payload[0];
|
||||
long actionType = bytesToLong(Arrays.copyOfRange(payload, 0, 1));
|
||||
if (actionType == 1) {
|
||||
System.out.println("VMG");
|
||||
} else if (actionType == 2) {
|
||||
System.out.println("SAILS IN");
|
||||
} else if (actionType == 3) {
|
||||
System.out.println("SAILS OUT");
|
||||
} else if (actionType == 4) {
|
||||
System.out.println("TACK/GYBE");
|
||||
} else if (actionType == 5) {
|
||||
System.out.println("UPWIND");
|
||||
} else if (actionType == 6) {
|
||||
System.out.println("DOWNWIND");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* takes an array of up to 7 bytes and returns a positive long constructed from the input bytes
|
||||
*
|
||||
|
||||
@@ -7,8 +7,8 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import seng302.model.stream.xml.generator.Race;
|
||||
import seng302.model.stream.xml.generator.Regatta;
|
||||
import seng302.model.stream.xml.generator.RaceXMLTemplate;
|
||||
import seng302.model.stream.xml.generator.RegattaXMLTemplate;
|
||||
import seng302.gameServer.messages.XMLMessageSubType;
|
||||
|
||||
/**
|
||||
@@ -20,8 +20,8 @@ public class XMLGenerator {
|
||||
private static final String BOATS_TEMPLATE_NAME = "boats.ftlh";
|
||||
private static final String RACE_TEMPLATE_NAME = "race.ftlh";
|
||||
private Configuration configuration;
|
||||
private Regatta regatta;
|
||||
private Race race;
|
||||
private RegattaXMLTemplate regatta;
|
||||
private RaceXMLTemplate race;
|
||||
|
||||
/**
|
||||
* Set up a configuration instance for Apache Freemake
|
||||
@@ -48,7 +48,7 @@ public class XMLGenerator {
|
||||
* Note: This must be set before a regatta message can be generated
|
||||
* @param regatta The race regatta
|
||||
*/
|
||||
public void setRegatta(Regatta regatta){
|
||||
public void setRegattaTemplate(RegattaXMLTemplate regatta) {
|
||||
this.regatta = regatta;
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ public class XMLGenerator {
|
||||
* Note: This must be set before a boat or race message can be generated
|
||||
* @param race The race
|
||||
*/
|
||||
public void setRace(Race race){
|
||||
public void setRaceTemplate(RaceXMLTemplate race) {
|
||||
this.race = race;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@ import seng302.model.mark.Corner;
|
||||
import seng302.model.mark.Mark;
|
||||
import seng302.model.stream.xml.parser.RaceXMLData;
|
||||
import seng302.model.stream.xml.parser.RegattaXMLData;
|
||||
import seng302.model.token.Token;
|
||||
import seng302.model.token.TokenType;
|
||||
|
||||
/**
|
||||
* Utilities for parsing XML documents
|
||||
@@ -182,12 +184,32 @@ public class XMLParser {
|
||||
Element docEle = doc.getDocumentElement();
|
||||
return new RaceXMLData(
|
||||
extractParticpantIDs(docEle),
|
||||
extractTokens(docEle),
|
||||
extractCompoundMarks(docEle),
|
||||
extractMarkOrder(docEle),
|
||||
extractCourseLimit(docEle)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts token data
|
||||
*/
|
||||
private static List<Token> extractTokens(Element docEle) {
|
||||
List<Token> tokens = new ArrayList<>();
|
||||
NodeList tokenList = docEle.getElementsByTagName("Tokens").item(0).getChildNodes();
|
||||
for (int i = 0; i < tokenList.getLength(); i++) {
|
||||
Node tokenNode = tokenList.item(i);
|
||||
if (tokenNode.getNodeName().equals("Token")) {
|
||||
String tokenType = getNodeAttributeString(tokenNode, "TokenType");
|
||||
Double lat = getNodeAttributeDouble(tokenNode, "TargetLat");
|
||||
Double lng = getNodeAttributeDouble(tokenNode, "TargetLng");
|
||||
tokens.add(new Token(TokenType.valueOf(tokenType), lat, lng));
|
||||
}
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts course limit data
|
||||
*/
|
||||
|
||||
@@ -18,6 +18,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import seng302.gameServer.messages.BoatAction;
|
||||
import seng302.gameServer.messages.BoatActionMessage;
|
||||
import seng302.gameServer.messages.ChatterMessage;
|
||||
import seng302.gameServer.messages.ClientType;
|
||||
import seng302.gameServer.messages.CustomizeRequestMessage;
|
||||
import seng302.gameServer.messages.CustomizeRequestType;
|
||||
@@ -33,8 +34,6 @@ import seng302.model.stream.packets.StreamPacket;
|
||||
*/
|
||||
public class ClientToServerThread implements Runnable {
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Functional interface for receiving packets from client socket.
|
||||
*/
|
||||
@@ -95,7 +94,7 @@ public class ClientToServerThread implements Runnable {
|
||||
|
||||
sendRegistrationRequest();
|
||||
|
||||
thread = new Thread(this);
|
||||
thread = new Thread(this, "ClientToServer");
|
||||
thread.start();
|
||||
}
|
||||
|
||||
@@ -283,9 +282,17 @@ public class ClientToServerThread implements Runnable {
|
||||
* @param message The given message type.
|
||||
*/
|
||||
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) {
|
||||
try {
|
||||
os.write(message.getBuffer());
|
||||
os.write(bytes);
|
||||
} catch (IOException e) {
|
||||
logger.warn("IOException on attempting to sendBoatAction from Client");
|
||||
notifyDisconnectListeners("Cannot communicate with server");
|
||||
@@ -294,7 +301,7 @@ public class ClientToServerThread implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
private void closeSocket() {
|
||||
public void closeSocket() {
|
||||
try {
|
||||
socket.close();
|
||||
socketOpen = false;
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
package seng302.visualiser;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
import javafx.application.Platform;
|
||||
@@ -12,12 +15,15 @@ import javafx.fxml.FXMLLoader;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Alert;
|
||||
import javafx.scene.control.Alert.AlertType;
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.input.KeyEvent;
|
||||
import javafx.scene.layout.Pane;
|
||||
import javafx.util.Pair;
|
||||
import seng302.gameServer.GameState;
|
||||
import seng302.gameServer.MainServerThread;
|
||||
import seng302.gameServer.messages.BoatAction;
|
||||
import seng302.gameServer.messages.BoatStatus;
|
||||
import seng302.gameServer.messages.YachtEventType;
|
||||
import seng302.model.ClientYacht;
|
||||
import seng302.model.RaceState;
|
||||
import seng302.model.stream.packets.StreamPacket;
|
||||
@@ -28,6 +34,7 @@ import seng302.model.stream.parser.RaceStatusData;
|
||||
import seng302.model.stream.parser.YachtEventData;
|
||||
import seng302.model.stream.xml.parser.RaceXMLData;
|
||||
import seng302.model.stream.xml.parser.RegattaXMLData;
|
||||
import seng302.utilities.Sounds;
|
||||
import seng302.utilities.StreamParser;
|
||||
import seng302.utilities.XMLParser;
|
||||
import seng302.visualiser.controllers.FinishScreenViewController;
|
||||
@@ -53,6 +60,8 @@ public class GameClient {
|
||||
private RaceState raceState = new RaceState();
|
||||
private LobbyController lobbyController;
|
||||
|
||||
private ArrayList<ClientYacht> finishedBoats = new ArrayList<>();
|
||||
|
||||
private ObservableList<String> clientLobbyList = FXCollections.observableArrayList();
|
||||
|
||||
/**
|
||||
@@ -142,11 +151,10 @@ public class GameClient {
|
||||
}
|
||||
|
||||
private void loadStartScreen() {
|
||||
socketThread.setSocketToClose();
|
||||
if (server != null) {
|
||||
server.terminate();
|
||||
server = null;
|
||||
if (socketThread != null) {
|
||||
socketThread.setSocketToClose();
|
||||
}
|
||||
|
||||
FXMLLoader fxmlLoader = new FXMLLoader(
|
||||
getClass().getResource("/views/StartScreenView.fxml"));
|
||||
try {
|
||||
@@ -195,9 +203,19 @@ public class GameClient {
|
||||
raceView = fxmlLoader.getController();
|
||||
ClientYacht player = allBoatsMap.get(socketThread.getClientId());
|
||||
raceView.loadRace(allBoatsMap, courseData, raceState, player);
|
||||
raceView.getSendPressedProperty().addListener((obs, old, isPressed) -> {
|
||||
if (isPressed) {
|
||||
formatAndSendChatMessage(raceView.readChatInput());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void loadFinishScreenView() {
|
||||
Sounds.stopMusic();
|
||||
Sounds.stopSoundEffects();
|
||||
Sounds.playFinishMusic();
|
||||
FXMLLoader fxmlLoader = loadFXMLToHolder("/views/FinishScreenView.fxml");
|
||||
FinishScreenViewController controller = fxmlLoader.getController();
|
||||
controller.setFinishers(raceState.getPlayerPositions());
|
||||
@@ -248,6 +266,7 @@ public class GameClient {
|
||||
courseData = XMLParser.parseRace(
|
||||
StreamParser.extractXmlMessage(packet)
|
||||
);
|
||||
|
||||
if (raceView != null) {
|
||||
raceView.updateRaceData(courseData);
|
||||
}
|
||||
@@ -278,8 +297,21 @@ public class GameClient {
|
||||
break;
|
||||
|
||||
case YACHT_EVENT_CODE:
|
||||
showCollisionAlert(StreamParser.extractYachtEventCode(packet));
|
||||
YachtEventData yachtEventData = StreamParser.extractYachtEventCode(packet);
|
||||
if (yachtEventData.getEventId() == YachtEventType.COLLISION.getCode()) {
|
||||
showCollisionAlert(StreamParser.extractYachtEventCode(packet));
|
||||
} else if (yachtEventData.getEventId() == YachtEventType.TOKEN.getCode()) {
|
||||
showPickUp();
|
||||
}
|
||||
break;
|
||||
|
||||
case CHATTER_TEXT:
|
||||
Pair<Integer, String> playerIdMessagePair = StreamParser
|
||||
.extractChatterText(packet);
|
||||
raceView.updateChatHistory(
|
||||
allBoatsMap.get(playerIdMessagePair.getKey()).getColour(),
|
||||
playerIdMessagePair.getValue()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -320,7 +352,6 @@ public class GameClient {
|
||||
if (allXMLReceived()) {
|
||||
ClientYacht clientYacht = allBoatsMap.get(roundingData.getBoatId());
|
||||
clientYacht.roundMark(
|
||||
courseData.getCompoundMarks().get(roundingData.getMarkId()),
|
||||
roundingData.getTimeStamp(),
|
||||
raceState.getRaceTime() - roundingData.getTimeStamp()
|
||||
);
|
||||
@@ -335,6 +366,9 @@ public class GameClient {
|
||||
for (ClientYacht yacht : allBoatsMap.values()) {
|
||||
if (yacht.getBoatStatus() != BoatStatus.FINISHED.getCode()) {
|
||||
raceFinished = false;
|
||||
} else if (!finishedBoats.contains(yacht)) {
|
||||
finishedBoats.add(yacht);
|
||||
Sounds.playFinishSound();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -350,6 +384,7 @@ public class GameClient {
|
||||
}
|
||||
|
||||
if (raceFinished) {
|
||||
Sounds.playFinishSound();
|
||||
close();
|
||||
loadFinishScreenView();
|
||||
}
|
||||
@@ -373,6 +408,12 @@ public class GameClient {
|
||||
* @param e The key event triggering this call
|
||||
*/
|
||||
private void keyPressed(KeyEvent e) {
|
||||
if (raceView.isChatInputFocused()) {
|
||||
if (e.getCode() == KeyCode.ENTER) {
|
||||
formatAndSendChatMessage(raceView.readChatInput());
|
||||
}
|
||||
return;
|
||||
}
|
||||
switch (e.getCode()) {
|
||||
case SPACE: // align with vmg
|
||||
socketThread.sendBoatAction(BoatAction.VMG); break;
|
||||
@@ -381,12 +422,16 @@ public class GameClient {
|
||||
case PAGE_DOWN: // downwind
|
||||
socketThread.sendBoatAction(BoatAction.DOWNWIND); break;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void keyReleased(KeyEvent e) {
|
||||
if (raceView.isChatInputFocused()) {
|
||||
return;
|
||||
}
|
||||
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)
|
||||
case SHIFT: // sails in/sails out
|
||||
@@ -407,13 +452,31 @@ public class GameClient {
|
||||
* Tells race view to show a collision animation.
|
||||
*/
|
||||
private void showCollisionAlert(YachtEventData yachtEventData) {
|
||||
// 33 is the agreed code to show collision
|
||||
if (yachtEventData.getEventId() == 33) {
|
||||
raceState.storeCollision(
|
||||
allBoatsMap.get(
|
||||
yachtEventData.getSubjectId().intValue()
|
||||
)
|
||||
Sounds.playCrashSound();
|
||||
raceState.storeCollision(
|
||||
allBoatsMap.get(
|
||||
yachtEventData.getSubjectId().intValue()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: 11/09/17 wmu16 - Add in functionality to viually indicate a pickup to a user
|
||||
private void showPickUp() {
|
||||
Sounds.playTokenPickupSound();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -36,7 +36,9 @@ import seng302.model.Limit;
|
||||
import seng302.model.mark.CompoundMark;
|
||||
import seng302.model.mark.Corner;
|
||||
import seng302.model.mark.Mark;
|
||||
import seng302.model.token.Token;
|
||||
import seng302.utilities.GeoUtility;
|
||||
import seng302.utilities.Sounds;
|
||||
import seng302.visualiser.fxObjects.AnnotationBox;
|
||||
import seng302.visualiser.fxObjects.BoatObject;
|
||||
import seng302.visualiser.fxObjects.CourseBoundary;
|
||||
@@ -64,6 +66,8 @@ public class GameView extends Pane {
|
||||
private double referencePointX, referencePointY;
|
||||
private double metersPerPixelX, metersPerPixelY;
|
||||
|
||||
private boolean isZoom = false;
|
||||
|
||||
private Text fpsDisplay = new Text();
|
||||
private Polygon raceBorder = new CourseBoundary();
|
||||
|
||||
@@ -81,6 +85,7 @@ public class GameView extends Pane {
|
||||
private Group boatObjectGroup = new Group();
|
||||
private Group trails = new Group();
|
||||
private Group markers = new Group();
|
||||
private Group tokens = new Group();
|
||||
private List<CompoundMark> course = new ArrayList<>();
|
||||
|
||||
private ImageView mapImage = new ImageView();
|
||||
@@ -100,7 +105,7 @@ public class GameView extends Pane {
|
||||
|
||||
private void zoomOut() {
|
||||
scaleFactor = 0.1;
|
||||
if (this.getScaleX() > 0.5) {
|
||||
if (this.isZoom && this.getScaleX() > 0.5) {
|
||||
this.setScaleX(this.getScaleX() - scaleFactor);
|
||||
this.setScaleY(this.getScaleY() - scaleFactor);
|
||||
}
|
||||
@@ -108,7 +113,7 @@ public class GameView extends Pane {
|
||||
|
||||
private void zoomIn() {
|
||||
scaleFactor = 0.1;
|
||||
if (this.getScaleX() < 2.5) {
|
||||
if (this.isZoom && this.getScaleX() < 2.5) {
|
||||
this.setScaleX(this.getScaleX() + scaleFactor);
|
||||
this.setScaleY(this.getScaleY() + scaleFactor);
|
||||
}
|
||||
@@ -140,7 +145,15 @@ public class GameView extends Pane {
|
||||
gameObjects.add(mapImage);
|
||||
gameObjects.add(raceBorder);
|
||||
gameObjects.add(markers);
|
||||
gameObjects.add(tokens);
|
||||
initializeTimer();
|
||||
this.sceneProperty().addListener(((observable, oldValue, scene) -> {
|
||||
if (scene != null) {
|
||||
setupZoom();
|
||||
} else {
|
||||
disableZoom();
|
||||
}
|
||||
}));
|
||||
|
||||
this.widthProperty().addListener(new ChangeListener<Number>() {
|
||||
@Override
|
||||
@@ -468,23 +481,51 @@ public class GameView extends Pane {
|
||||
raceBorder.getPoints().setAll(boundaryPoints);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces all tokens in the course with those passed in
|
||||
*
|
||||
* @param newTokens the tokens to be put on the course.
|
||||
*/
|
||||
public void updateTokens(List<Token> newTokens) {
|
||||
|
||||
List<Marker> mapTokens = new ArrayList<>();
|
||||
|
||||
for (Token token : newTokens) {
|
||||
Point2D location = findScaledXY(token.getLat(), token.getLng());
|
||||
Marker thisMarker = new Marker(Color.YELLOW);
|
||||
thisMarker.setLayoutX(location.getX());
|
||||
thisMarker.setLayoutY(location.getY());
|
||||
mapTokens.add(thisMarker);
|
||||
}
|
||||
Platform.runLater(() -> {
|
||||
tokens.getChildren().clear();
|
||||
tokens.getChildren().addAll(mapTokens);
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: 16/08/17 initialize zooming internal to GameView only
|
||||
/**
|
||||
* Enables zoom. Has to be called after this is added to a scene.
|
||||
*/
|
||||
public void enableZoom () {
|
||||
System.out.println("enable zoom");
|
||||
if (this.getScene() != null) {
|
||||
System.out.println("can zoom");
|
||||
this.getScene().addEventHandler(KeyEvent.KEY_PRESSED, (event) -> {
|
||||
if (event.getCode() == KeyCode.Z) {
|
||||
zoomIn();
|
||||
} else if (event.getCode() == KeyCode.X) {
|
||||
zoomOut();
|
||||
}
|
||||
});
|
||||
}
|
||||
private void setupZoom() {
|
||||
this.getScene().addEventHandler(KeyEvent.KEY_PRESSED, (event) -> {
|
||||
if (event.getCode() == KeyCode.Z) {
|
||||
zoomIn();
|
||||
} else if (event.getCode() == KeyCode.X) {
|
||||
zoomOut();
|
||||
}
|
||||
});
|
||||
enableZoom();
|
||||
}
|
||||
|
||||
public void enableZoom() {
|
||||
isZoom = true;
|
||||
}
|
||||
|
||||
public void disableZoom() {
|
||||
isZoom = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rescales the race to the size of the window.
|
||||
*
|
||||
@@ -817,15 +858,11 @@ public class GameView extends Pane {
|
||||
playerYacht.addMarkRoundingListener(this::updateMarkArrows);
|
||||
}
|
||||
|
||||
private void updateMarkArrows (ClientYacht yacht, CompoundMark compoundMark, int legNumber) {
|
||||
private void updateMarkArrows (ClientYacht yacht, int legNumber) {
|
||||
//Only show arrows for this and next leg.
|
||||
if (compoundMark != null) {
|
||||
for (Mark mark : compoundMark.getMarks()) {
|
||||
markerObjects.get(mark).showNextExitArrow();
|
||||
}
|
||||
}
|
||||
CompoundMark nextMark = null;
|
||||
if (legNumber < course.size() - 1) {
|
||||
Sounds.playMarkRoundingSound();
|
||||
nextMark = course.get(legNumber);
|
||||
for (Mark mark : nextMark.getMarks()) {
|
||||
markerObjects.get(mark).showNextEnterArrow();
|
||||
@@ -839,6 +876,14 @@ public class GameView extends Pane {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (legNumber - 1 >= 0) {
|
||||
CompoundMark thisMark = course.get(Math.max(0, legNumber - 1));
|
||||
if (thisMark != nextMark) {
|
||||
for (Mark mark : thisMark.getMarks()) {
|
||||
markerObjects.get(mark).showNextExitArrow();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,6 +7,7 @@ import javafx.scene.control.TextField;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.stage.Stage;
|
||||
import seng302.gameServer.messages.CustomizeRequestType;
|
||||
import seng302.utilities.Sounds;
|
||||
import seng302.visualiser.ClientToServerThread;
|
||||
|
||||
public class CustomizationController {
|
||||
@@ -34,7 +35,8 @@ public class CustomizationController {
|
||||
|
||||
@FXML
|
||||
public void submitCustomization() {
|
||||
System.out.println("Attempting to send");
|
||||
Sounds.playButtonClick();
|
||||
// System.out.println("Attempting to send");
|
||||
socketThread.sendCustomizationRequest(CustomizeRequestType.NAME, nameField.getText().getBytes());
|
||||
// TODO: 16/08/17 ajm412: Turn colors into byte array.
|
||||
Color color = boatColorPicker.getValue();
|
||||
|
||||
@@ -15,10 +15,12 @@ import javafx.fxml.Initializable;
|
||||
import javafx.scene.control.TableColumn;
|
||||
import javafx.scene.control.TableView;
|
||||
import javafx.scene.control.cell.PropertyValueFactory;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import javafx.scene.layout.Pane;
|
||||
import seng302.model.ClientYacht;
|
||||
import seng302.utilities.Sounds;
|
||||
|
||||
public class FinishScreenViewController implements Initializable {
|
||||
|
||||
@@ -85,6 +87,11 @@ public class FinishScreenViewController implements Initializable {
|
||||
}
|
||||
|
||||
public void switchToStartScreenView() {
|
||||
Sounds.playButtonClick();
|
||||
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.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.text.Text;
|
||||
import javafx.stage.Stage;
|
||||
@@ -23,6 +24,7 @@ import seng302.gameServer.GameStages;
|
||||
import seng302.gameServer.GameState;
|
||||
import seng302.model.Colors;
|
||||
import seng302.model.RaceState;
|
||||
import seng302.utilities.Sounds;
|
||||
import seng302.visualiser.ClientToServerThread;
|
||||
|
||||
/**
|
||||
@@ -31,6 +33,10 @@ import seng302.visualiser.ClientToServerThread;
|
||||
*/
|
||||
public class LobbyController {
|
||||
|
||||
public void playButtonHoverSound(MouseEvent mouseEvent) {
|
||||
Sounds.playHoverSound();
|
||||
}
|
||||
|
||||
public enum CloseStatus {
|
||||
LEAVE,
|
||||
READY
|
||||
@@ -153,6 +159,7 @@ public class LobbyController {
|
||||
|
||||
@FXML
|
||||
public void customize() {
|
||||
Sounds.playButtonClick();
|
||||
Parent root;
|
||||
try {
|
||||
FXMLLoader fxmlLoader = new FXMLLoader(LobbyController.class.getResource("/views/customizeView.fxml"));
|
||||
@@ -184,6 +191,7 @@ public class LobbyController {
|
||||
|
||||
@FXML
|
||||
public void leaveLobbyButtonPressed() {
|
||||
Sounds.playButtonClick();
|
||||
// TODO: 10/07/17 wmu16 - Finish function!
|
||||
GameState.setCurrentStage(GameStages.CANCELLED);
|
||||
// TODO: 20/07/17 wmu16 - Implement some way of terminating the game
|
||||
@@ -193,6 +201,7 @@ public class LobbyController {
|
||||
|
||||
@FXML
|
||||
public void readyButtonPressed() {
|
||||
Sounds.playButtonClick();
|
||||
GameState.setCurrentStage(GameStages.PRE_RACE);
|
||||
// Do countdown logic here
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import java.util.TimerTask;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javafx.animation.Timeline;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.ReadOnlyBooleanProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
@@ -25,6 +26,8 @@ import javafx.scene.control.Button;
|
||||
import javafx.scene.control.CheckBox;
|
||||
import javafx.scene.control.ComboBox;
|
||||
import javafx.scene.control.Slider;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import javafx.scene.layout.Pane;
|
||||
import javafx.scene.layout.VBox;
|
||||
@@ -41,18 +44,31 @@ import seng302.model.RaceState;
|
||||
import seng302.model.mark.CompoundMark;
|
||||
import seng302.model.mark.Mark;
|
||||
import seng302.model.stream.xml.parser.RaceXMLData;
|
||||
import seng302.model.token.Token;
|
||||
import seng302.utilities.Sounds;
|
||||
import seng302.visualiser.GameView;
|
||||
import seng302.visualiser.controllers.annotations.Annotation;
|
||||
import seng302.visualiser.controllers.annotations.ImportantAnnotationController;
|
||||
import seng302.visualiser.controllers.annotations.ImportantAnnotationDelegate;
|
||||
import seng302.visualiser.controllers.annotations.ImportantAnnotationsState;
|
||||
import seng302.visualiser.fxObjects.BoatObject;
|
||||
import seng302.visualiser.fxObjects.ChatHistory;
|
||||
|
||||
/**
|
||||
* Controller class that manages the display of a race
|
||||
*/
|
||||
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
|
||||
private LineChart<String, Double> raceSparkLine;
|
||||
@FXML
|
||||
@@ -85,12 +101,17 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
||||
private GameView gameView;
|
||||
private RaceState raceState;
|
||||
|
||||
private ChatHistory chatHistory;
|
||||
|
||||
private Timeline timerTimeline;
|
||||
private Timer timer = new Timer();
|
||||
private List<Series<String, Double>> sparkLineData = new ArrayList<>();
|
||||
private ImportantAnnotationsState importantAnnotations;
|
||||
private ObservableList<ClientYacht> selectionComboBoxList = FXCollections.observableArrayList();
|
||||
|
||||
public void initialize() {
|
||||
Sounds.stopMusic();
|
||||
Sounds.playRaceMusic();
|
||||
// Load a default important annotation state
|
||||
importantAnnotations = new ImportantAnnotationsState();
|
||||
|
||||
@@ -101,10 +122,30 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
||||
raceSparkLine.visibleProperty().setValue(false);
|
||||
raceSparkLine.getYAxis().setAutoRanging(false);
|
||||
sparklineYAxis.setTickMarkVisible(false);
|
||||
|
||||
positionVbox.getStylesheets().add(getClass().getResource("/css/master.css").toString());
|
||||
|
||||
selectAnnotationBtn.setOnAction(event -> loadSelectAnnotationView());
|
||||
chatInput.lengthProperty().addListener((obs, oldLen, newLen) -> {
|
||||
if (newLen.intValue() > CHAT_LIMIT) {
|
||||
chatInput.setText(chatInput.getText().substring(0, CHAT_LIMIT));
|
||||
}
|
||||
});
|
||||
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 (
|
||||
@@ -116,12 +157,6 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
||||
this.markers = raceData.getCompoundMarks();
|
||||
this.raceState = raceState;
|
||||
|
||||
initializeUpdateTimer();
|
||||
initialiseFPSCheckBox();
|
||||
initialiseAnnotationSlider();
|
||||
initialiseBoatSelectionComboBox();
|
||||
initialiseSparkLine();
|
||||
|
||||
raceState.getPlayerPositions().addListener((ListChangeListener<ClientYacht>) c -> {
|
||||
while (c.next()) {
|
||||
if (c.wasPermutated()) {
|
||||
@@ -137,10 +172,10 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
||||
Platform.runLater(() -> contentAnchorPane.getChildren().add(0, gameView));
|
||||
gameView.setBoats(new ArrayList<>(participants.values()));
|
||||
gameView.updateBorder(raceData.getCourseLimit());
|
||||
gameView.updateTokens(raceData.getTokens());
|
||||
gameView.updateCourse(
|
||||
new ArrayList<>(raceData.getCompoundMarks().values()), raceData.getMarkSequence()
|
||||
);
|
||||
gameView.enableZoom();
|
||||
gameView.setBoatAsPlayer(player);
|
||||
gameView.startRace();
|
||||
|
||||
@@ -155,6 +190,20 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
||||
updateWindDirection(raceState.windDirectionProperty().doubleValue());
|
||||
updateWindSpeed(raceState.getWindSpeed());
|
||||
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();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -305,13 +354,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()
|
||||
// )
|
||||
// );
|
||||
}
|
||||
|
||||
|
||||
@@ -533,10 +575,8 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
||||
* for the combobox to take action upon selection
|
||||
*/
|
||||
private void initialiseBoatSelectionComboBox() {
|
||||
yachtSelectionComboBox.setItems(
|
||||
FXCollections.observableArrayList(participants.values())
|
||||
);
|
||||
//Null check is if the listener is fired but nothing selected
|
||||
selectionComboBoxList.setAll(participants.values());
|
||||
yachtSelectionComboBox.setItems(selectionComboBoxList);
|
||||
yachtSelectionComboBox.valueProperty().addListener((obs, lastSelection, selectedBoat) -> {
|
||||
if (selectedBoat != null) {
|
||||
gameView.selectBoat(selectedBoat);
|
||||
@@ -621,5 +661,26 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
||||
public void updateRaceData (RaceXMLData raceData) {
|
||||
this.courseData = raceData;
|
||||
gameView.updateBorder(raceData.getCourseLimit());
|
||||
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.util.Enumeration;
|
||||
import java.util.ResourceBundle;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.control.ToggleButton;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import seng302.gameServer.GameState;
|
||||
import seng302.utilities.Sounds;
|
||||
import seng302.visualiser.GameClient;
|
||||
|
||||
/**
|
||||
@@ -20,114 +24,56 @@ import seng302.visualiser.GameClient;
|
||||
*/
|
||||
public class StartScreenController implements Initializable {
|
||||
|
||||
@FXML
|
||||
private ToggleButton muteMusicButton;
|
||||
@FXML
|
||||
private ToggleButton muteSoundsButton;
|
||||
@FXML
|
||||
private TextField ipTextField;
|
||||
@FXML
|
||||
private TextField portTextField;
|
||||
@FXML
|
||||
private GridPane startScreen2;
|
||||
@FXML
|
||||
private AnchorPane holder;
|
||||
|
||||
GameClient gameClient;
|
||||
private GameClient gameClient;
|
||||
|
||||
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);
|
||||
}
|
||||
//
|
||||
// /**
|
||||
// * 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:
|
||||
* 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
|
||||
* Creates an instance of GameClient and runs it as a host.
|
||||
*/
|
||||
@FXML
|
||||
public void hostButtonPressed() {
|
||||
// new GameState(getLocalHostIp());
|
||||
Sounds.playButtonClick();
|
||||
gameClient = new GameClient(holder);
|
||||
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:
|
||||
* 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.
|
||||
* Creates an instance of GameClient and runs it has a client.
|
||||
*/
|
||||
@FXML
|
||||
public void connectButtonPressed() {
|
||||
// TODO: 10/07/17 wmu16 - Finish function
|
||||
Sounds.playButtonClick();
|
||||
gameClient = new GameClient(holder);
|
||||
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.
|
||||
@@ -162,7 +108,30 @@ public class StartScreenController implements Initializable {
|
||||
if (ipAddress == null) {
|
||||
System.out.println("[HOST] Cannot obtain local host ip address.");
|
||||
}
|
||||
// ClientState.setHostIp(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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user