Lobby view will switch to race view when received race status packet with race start type.

Ready button can only be pressed by host. Once pressed, it will send out race status packet with race start to all clients.

#story[1055]
This commit is contained in:
Zhi You Tan
2017-07-25 00:01:59 +12:00
parent 8b0af5bb62
commit d1d659b698
9 changed files with 97 additions and 401 deletions
@@ -215,6 +215,11 @@ public class ClientPacketParser {
clientBoat.setEstimateTimeAtNextMark(estTimeAtNextMark); clientBoat.setEstimateTimeAtNextMark(estTimeAtNextMark);
clientBoat.setEstimateTimeAtFinish(estTimeAtFinish); clientBoat.setEstimateTimeAtFinish(estTimeAtFinish);
} }
// 3 is race started
if (raceStatus == 3) {
ClientState.setRaceStarted(true);
}
} }
private static void setBoatLegPosition(Yacht updatingBoat, Integer leg){ private static void setBoatLegPosition(Yacht updatingBoat, Integer leg){
@@ -15,10 +15,6 @@ public class ClientStateQueryingRunnable extends Observable implements Runnable
@Override @Override
public void run() { public void run() {
while(!terminate) { while(!terminate) {
// if (ClientState.isRaceStarted() && ClientState.isConnectedToHost()) {
// setChanged();
// notifyObservers();
// }
// Sleeping the thread so it will respond to the if statement below // Sleeping the thread so it will respond to the if statement below
// if you know a better fix, pls tell me :) -ryan // if you know a better fix, pls tell me :) -ryan
try { try {
@@ -26,9 +22,16 @@ public class ClientStateQueryingRunnable extends Observable implements Runnable
} catch (InterruptedException e) { } catch (InterruptedException e) {
e.printStackTrace(); e.printStackTrace();
} }
if (ClientState.isRaceStarted() && ClientState.isConnectedToHost()) {
setChanged();
notifyObservers("game started");
terminate();
}
if (ClientState.isDirtyState()) { if (ClientState.isDirtyState()) {
setChanged(); setChanged();
notifyObservers(); notifyObservers("update players");
ClientState.setDirtyState(false); ClientState.setDirtyState(false);
} }
} }
@@ -95,11 +95,9 @@ public class ClientToServerThread implements Runnable {
// TODO: 17/07/17 wmu16 - Fix this or maybe we dont need to go through the main server at all!?!? // TODO: 17/07/17 wmu16 - Fix this or maybe we dont need to go through the main server at all!?!?
// packetBufferDelegate.addToBuffer(new StreamPacket(type, payloadLength, timeStamp, payload)); // packetBufferDelegate.addToBuffer(new StreamPacket(type, payloadLength, timeStamp, payload));
} else { } else {
System.err.println("Packet has been dropped"); clientLog("Packet has been dropped", 1);
} }
} }
} catch (Exception e) { } catch (Exception e) {
closeSocket(); closeSocket();
e.printStackTrace(); e.printStackTrace();
@@ -18,6 +18,7 @@ import javafx.collections.ObservableList;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader; import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable; import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.ListView; import javafx.scene.control.ListView;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
@@ -29,19 +30,20 @@ import seng302.client.ClientState;
import seng302.client.ClientStateQueryingRunnable; import seng302.client.ClientStateQueryingRunnable;
import seng302.gameServer.GameStages; import seng302.gameServer.GameStages;
import seng302.gameServer.GameState; import seng302.gameServer.GameState;
import seng302.gameServer.MainServerThread;
/** /**
* A class describing the actions of the lobby screen * A class describing the actions of the lobby screen
* Created by wmu16 on 10/07/17. * Created by wmu16 on 10/07/17.
*/ */
public class LobbyController implements Initializable, Observer{ public class LobbyController implements Initializable, Observer{
@FXML @FXML
private GridPane lobbyScreen; private GridPane lobbyScreen;
@FXML @FXML
private Text lobbyIpText; private Text lobbyIpText;
@FXML @FXML
private Button readyButton;
@FXML
private ListView firstListView; private ListView firstListView;
@FXML @FXML
private ListView secondListView; private ListView secondListView;
@@ -85,6 +87,9 @@ public class LobbyController implements Initializable, Observer{
private static ObservableList<String> eighthCompetitor = FXCollections.observableArrayList(); private static ObservableList<String> eighthCompetitor = FXCollections.observableArrayList();
private ClientStateQueryingRunnable clientStateQueryingRunnable; private ClientStateQueryingRunnable clientStateQueryingRunnable;
private Boolean switchedPane = false;
private MainServerThread mainServerThread;
private void setContentPane(String jfxUrl) { private void setContentPane(String jfxUrl) {
try { try {
AnchorPane contentPane = (AnchorPane) lobbyScreen.getParent(); AnchorPane contentPane = (AnchorPane) lobbyScreen.getParent();
@@ -102,10 +107,14 @@ public class LobbyController implements Initializable, Observer{
@Override @Override
public void initialize(URL location, ResourceBundle resources) { public void initialize(URL location, ResourceBundle resources) {
if (ClientState.isHost()) if (ClientState.isHost()) {
lobbyIpText.setText("Lobby Host IP: " + ClientState.getHostIp()); lobbyIpText.setText("Lobby Host IP: " + ClientState.getHostIp());
else readyButton.setDisable(false);
}
else {
lobbyIpText.setText("Connected to IP: "); lobbyIpText.setText("Connected to IP: ");
readyButton.setDisable(true);
}
initialiseListView(); initialiseListView();
// initialiseLobbyControllerThread(); // initialiseLobbyControllerThread();
// initialiseImageView(); // parrot gif init // initialiseImageView(); // parrot gif init
@@ -124,9 +133,12 @@ public class LobbyController implements Initializable, Observer{
Platform.runLater(new Runnable() { Platform.runLater(new Runnable() {
@Override @Override
public void run() { public void run() {
// switchToRaceView(); if (arg.equals("game started") && !switchedPane) {
initialiseListView(); switchToRaceView();
// clientStateQueryingRunnable.terminate(); }
if (arg.equals(("update players"))) {
initialiseListView();
}
} }
}); });
} }
@@ -163,8 +175,6 @@ public class LobbyController implements Initializable, Observer{
} }
} }
firstListView.setItems(firstCompetitor); firstListView.setItems(firstCompetitor);
secondListView.setItems(secondCompetitor); secondListView.setItems(secondCompetitor);
thirdListView.setItems(thirdCompetitor); thirdListView.setItems(thirdCompetitor);
@@ -220,11 +230,19 @@ public class LobbyController implements Initializable, Observer{
@FXML @FXML
public void readyButtonPressed() { public void readyButtonPressed() {
setContentPane("/views/RaceView.fxml"); // setContentPane("/views/RaceView.fxml");
GameState.setCurrentStage(GameStages.RACING); GameState.setCurrentStage(GameStages.RACING);
mainServerThread.startGame();
} }
private void switchToRaceView() { private void switchToRaceView() {
setContentPane("/views/RaceView.fxml"); if (!switchedPane) {
switchedPane = true;
setContentPane("/views/RaceView.fxml");
}
}
public void setMainServerThread(MainServerThread mainServerThread) {
this.mainServerThread = mainServerThread;
} }
} }
@@ -72,14 +72,15 @@ public class StartScreenController {
String ipAddress = InetAddress.getLocalHost().getHostAddress(); String ipAddress = InetAddress.getLocalHost().getHostAddress();
// get the lobby controller so that we can pass the game server thread to it // get the lobby controller so that we can pass the game server thread to it
new GameState(getLocalHostIp()); new GameState(getLocalHostIp());
new MainServerThread(); MainServerThread mainServerThread = new MainServerThread();
ClientState.setHost(true); ClientState.setHost(true);
// host will connect and handshake to itself after setting up the server // 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? // TODO: 24/07/17 wmu16 - Make port number some static global type constant?
ClientToServerThread clientToServerThread = new ClientToServerThread(ClientState.getHostIp(), 4942); ClientToServerThread clientToServerThread = new ClientToServerThread(ClientState.getHostIp(), 4942);
ClientState.setConnectedToHost(true); ClientState.setConnectedToHost(true);
controller.setClientToServerThread(clientToServerThread); controller.setClientToServerThread(clientToServerThread);
setContentPane("/views/LobbyView.fxml"); LobbyController lobbyController = (LobbyController) setContentPane("/views/LobbyView.fxml");
lobbyController.setMainServerThread(mainServerThread);
} catch (Exception e) { } catch (Exception e) {
Alert alert = new Alert(AlertType.ERROR); Alert alert = new Alert(AlertType.ERROR);
alert.setHeaderText("Cannot host"); alert.setHeaderText("Cannot host");
@@ -1,372 +0,0 @@
package seng302.gameServer;
import seng302.models.Player;
import seng302.models.Yacht;
import seng302.server.messages.*;
import seng302.server.simulator.Boat;
import seng302.server.simulator.Simulator;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.SocketOption;
import java.net.SocketOptions;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.*;
public class GameServerThread implements Runnable, Observer, ClientConnectionDelegate{
private static final Integer MAX_NUM_PLAYERS = 10;
public static final int PORT_NUMBER = 4942;
private Boolean hosting = true;
private ServerSocketChannel server;
private long startTime;
private short seqNum;
private final int RACE_STATUS_PERIOD = 1000/2;
private final int RACE_START_STATUS_PERIOD = 1000;
private final int BOAT_LOCATION_PERIOD = 1000/5;
private final int TIME_TILL_RACE_START = 20*1000;
private static final int LOG_LEVEL = 1;
public GameServerThread(String threadName){
Thread runner = new Thread(this, threadName);
runner.setDaemon(true);
seqNum = 0;
runner.start();
}
public static void serverLog(String message, int logLevel){
if(logLevel <= LOG_LEVEL){
System.out.println("[SERVER] " + message);
}
}
/**
* Creates and returns an XML Message from the file specified
* @param fileName The source XML file
* @param type The XML Message type
* @return The XML Message
*/
private Message getXmlMessage(String fileName, XMLMessageSubType type){
String fileContents = null;
try {
InputStream thisStream = this.getClass().getResourceAsStream(fileName);
fileContents = new String(org.apache.commons.io.IOUtils.toByteArray(thisStream));
} catch (IOException e) {
e.printStackTrace();
} catch (NullPointerException e){
return null;
}
if (fileContents != null){
return new XMLMessage(fileContents, type, seqNum);
}
return null;
}
/**
* @return Get a race status message for the current race
*/
private Message getRaceStatusMessage(){
List<BoatSubMessage> boatSubMessages = new ArrayList<>();
BoatStatus boatStatus;
RaceStatus raceStatus;
boolean thereAreBoatsNotFinished = false;
for (Player player : GameState.getPlayers()){
Yacht y = player.getYacht();
if (GameState.getCurrentStage() == GameStages.PRE_RACE){
boatStatus = BoatStatus.PRESTART;
thereAreBoatsNotFinished = true;
}
else if(false){ //@TODO if boat has finished
boatStatus = BoatStatus.FINISHED;
}
else{
boatStatus = BoatStatus.PRESTART;
thereAreBoatsNotFinished = true;
}
BoatSubMessage m = new BoatSubMessage(y.getSourceId(), boatStatus, y.getLastMarkRounded().getId(), 0, 0, 1234l, 1234l);
boatSubMessages.add(m);
}
if (thereAreBoatsNotFinished){
if (GameState.getCurrentStage() == GameStages.RACING){
raceStatus = RaceStatus.STARTED;
}
else{
long currentTime = System.currentTimeMillis();
long timeDifference = startTime - currentTime;
if (timeDifference > 60*3){
raceStatus = RaceStatus.PRESTART;
}
else if (timeDifference > 60){
raceStatus = RaceStatus.WARNING;
}
else{
raceStatus = RaceStatus.PREPARATORY;
}
}
}
else{
raceStatus = RaceStatus.TERMINATED;
}
return new RaceStatusMessage(1, raceStatus, startTime, WindDirection.SOUTH,
100, GameState.getPlayers().size(), RaceType.MATCH_RACE, 1, boatSubMessages);
}
/**
* Start sending race start status messages until race starts
*/
private void startSendingRaceStartStatusMessages(){
Timer t = new Timer();
t.schedule(new TimerTask() {
@Override
public void run() {
Message raceStartStatusMessage = new RaceStartStatusMessage(seqNum, startTime , 1,
RaceStartNotificationType.SET_RACE_START_TIME);
try {
if (startTime < System.currentTimeMillis() && GameState.getCurrentStage() != GameStages.RACING){
}
else{
broadcast(raceStartStatusMessage);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}, 0, RACE_START_STATUS_PERIOD);
}
/**
* Start sending race start status messages until race starts
*/
private void startSendingRaceStatusMessages(){
Timer t = new Timer();
t.schedule(new TimerTask() {
@Override
public void run() {
Message raceStatusMessage = getRaceStatusMessage();
try {
broadcast(raceStatusMessage);
} catch (IOException e) {
e.printStackTrace();
}
}
}, 0, RACE_STATUS_PERIOD);
}
/**
* Sends the race, boat, and regatta XML files to the client
*/
private void sendXml(){
try{
Message raceData = getXmlMessage("/server_config/race.xml", XMLMessageSubType.RACE);
Message boatData = getXmlMessage("/server_config/boats.xml", XMLMessageSubType.BOAT);
Message regatta = getXmlMessage("/server_config/regatta.xml", XMLMessageSubType.REGATTA);
if (raceData != null){
broadcast(raceData);
}
if (boatData != null){
broadcast(boatData);
}
if (regatta != null){
broadcast(regatta);
}
} catch (IOException e) {
serverLog("Couldn't send an XML Message: " + e.getMessage(), 0);
}
}
/**
* Send the post-start race course information
*/
private void sendPostStartCourseXml(){
Timer t = new Timer();
t.schedule(new TimerTask() {
@Override
public void run() {
try {
Message raceData = getXmlMessage("/server_config/courseLimits.xml", XMLMessageSubType.RACE);
if (raceData != null) {
broadcast(raceData);
}
}catch (IOException e) {
serverLog("Couldn't send an XML Message: " + e.getMessage(), 0);
}
}
},1000);
//Delays the new course xml data for 25 seconds so the boats are able to pass the starting line
}
public void run() {
ServerListenThread serverListenThread;
HeartbeatThread heartbeatThread;
Boolean serverIsSendingMessages = false;
try{
server = ServerSocketChannel.open();
server.socket().bind(new InetSocketAddress("localhost", PORT_NUMBER));
// serverListenThread = new ServerListenThread(server, this);
heartbeatThread = new HeartbeatThread(this);
heartbeatThread.start();
// serverListenThread.start();
}
catch (IOException e){
serverLog("Failed to bind socket: " + e.getMessage(), 0);
}
while (hosting) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (GameState.getCurrentStage() == GameStages.RACING && !serverIsSendingMessages) {
serverLog("Race Started", 0);
sendXml();
startSendingRaceStartStatusMessages();
//startSendingRaceStatusMessages();
sendPostStartCourseXml();
serverIsSendingMessages = true;
}
else if (GameState.getCurrentStage() == GameStages.FINISHED) {
serverLog("Race Finished", 0);
}
startTime = System.currentTimeMillis() + TIME_TILL_RACE_START;
}
}
// /**
// * Start sending static boat position updates when race has finished
// */
// private void startSendingRaceFinishedBoatPositions(){
// Timer t = new Timer();
// t.schedule(new TimerTask() {
// @Override
// public void run() {
// try {
// for (Boat b : raceSimulator.getBoats()){
// Message m = new BoatLocationMessage(b.getSourceID(), seqNum, b.getLat(),
// b.getLng(), b.getLastPassedCorner().getBearingToNextCorner(),
// ((long) 0));
//
// server.send(m);
// }
//
// } catch (IOException e) {
// e.printStackTrace();
// }
// }
// }, 0, BOAT_LOCATION_PERIOD);
// }
/**
* A client has tried to connect to the server
* @param player The player that connected
*/
public void clientConnected(Player player) {
if (GameState.getPlayers().size() < MAX_NUM_PLAYERS && GameState.getCurrentStage() == GameStages.LOBBYING) {
serverLog("Player Connected", 0);
GameState.addPlayer(player);
sendXml();
}
}
@Override
public void clientConnected(ServerToClientThread serverToClientThread) {
}
/**
* A player has left the game, remove the player from the GameState
* @param player The player that left
*/
@Override
public void clientDisconnected(Player player) {
serverLog("Player disconnected", 0);
GameState.removePlayer(player);
sendXml();
}
void broadcast(Message message) throws IOException{
for(Player player : GameState.getPlayers()) {
//heh
player.getSocket().getOutputStream().write(message.getBuffer());
}
seqNum++;
}
/**
* Send a boat location message when they are updated by the simulator
* @param o .
* @param arg .
*/
@Override
@SuppressWarnings("unchecked")
public void update(Observable o, Object arg) {
/* Only send if server started
// TODO: I don't understand why i need to check server is null or not ... confused - haoming 2/5/17
if(server == null || !server.isStarted()){
return;
}
int numOfBoatsFinished = 0;
for (Boat boat : (List<Boat>) arg){
try {
if (boat.isFinished()) {
numOfBoatsFinished ++;
if (!boatsFinished.get(boat.getSourceID())) {
boatsFinished.put(boat.getSourceID(), true);
}
}
Message m = new BoatLocationMessage(boat.getSourceID(), 1, boat.getLat(),
boat.getLng(), boat.getLastPassedCorner().getBearingToNextCorner(),
((long) boat.getSpeed()));
broadcast(m);
} catch (IOException e) {
serverLog("Couldn't send a boat status message", 3);
return;
}
catch (NullPointerException e){
e.printStackTrace();
}*/
}
// if (numOfBoatsFinished == ((List<Boat>) arg).size()) {
// startSendingRaceFinishedBoatPositions();
// }
//}
public void terminateGame() {
try {
//TODO: for now, I just close the socket, but i think we should terminate the whole thread instead. -hyi25 13 July
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@@ -99,9 +99,6 @@ public class MainServerThread extends Observable implements Runnable, PacketBuff
} }
} }
public void updateClients() { public void updateClients() {
for (ServerToClientThread serverToClientThread : serverToClientThreads) { for (ServerToClientThread serverToClientThread : serverToClientThreads) {
serverToClientThread.updateClient(); serverToClientThread.updateClient();
@@ -146,4 +143,9 @@ public class MainServerThread extends Observable implements Runnable, PacketBuff
// sendXml(); // sendXml();
} }
public void startGame() {
for (ServerToClientThread serverToClientThread : serverToClientThreads) {
serverToClientThread.sendRaceStatusMessage();
}
}
} }
@@ -8,6 +8,7 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.Socket; import java.net.Socket;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import java.util.Observable; import java.util.Observable;
import java.util.Observer; import java.util.Observer;
import java.util.Random; import java.util.Random;
@@ -23,12 +24,18 @@ import seng302.models.xml.Regatta;
import seng302.models.xml.XMLGenerator; import seng302.models.xml.XMLGenerator;
import seng302.server.messages.BoatActionType; import seng302.server.messages.BoatActionType;
import seng302.server.messages.BoatLocationMessage; import seng302.server.messages.BoatLocationMessage;
import seng302.server.messages.BoatStatus;
import seng302.server.messages.BoatSubMessage;
import seng302.server.messages.Message; import seng302.server.messages.Message;
import java.io.*; import java.io.*;
import java.net.Socket; import java.net.Socket;
import java.util.zip.CRC32; import java.util.zip.CRC32;
import java.util.zip.Checksum; import java.util.zip.Checksum;
import seng302.server.messages.RaceStatus;
import seng302.server.messages.RaceStatusMessage;
import seng302.server.messages.RaceType;
import seng302.server.messages.WindDirection;
import seng302.server.messages.XMLMessage; import seng302.server.messages.XMLMessage;
import seng302.server.messages.XMLMessageSubType; import seng302.server.messages.XMLMessageSubType;
import seng302.server.messages.XMLMessage; import seng302.server.messages.XMLMessage;
@@ -172,6 +179,8 @@ public class ServerToClientThread implements Runnable, Observer {
// message = new XMLMessage(xml, XMLMessageSubType.BOAT, 0); // message = new XMLMessage(xml, XMLMessageSubType.BOAT, 0);
// sendMessage(message); // sendMessage(message);
// System.out.println("[server] send message 4 " + message); // System.out.println("[server] send message 4 " + message);
// sendMessage(getRaceStatusMessage());
// System.out.println("sent race status");
//------- //-------
while(true) { while(true) {
@@ -215,7 +224,7 @@ public class ServerToClientThread implements Runnable, Observer {
break; break;
} }
} else { } else {
System.err.println("Packet has been dropped"); serverLog("Packet has been dropped", 1);
} }
} }
} catch (Exception e) { } catch (Exception e) {
@@ -359,10 +368,42 @@ public class ServerToClientThread implements Runnable, Observer {
} }
} }
public Thread getThread() { public Thread getThread() {
return thread; return thread;
} }
public void sendRaceStatusMessage(){
// variables taken from GameServerThread
int TIME_TILL_RACE_START = 20*1000;
long startTime = System.currentTimeMillis() + TIME_TILL_RACE_START;
List<BoatSubMessage> boatSubMessages = new ArrayList<>();
BoatStatus boatStatus;
RaceStatus raceStatus;
for (Player player : GameState.getPlayers()){
Yacht y = player.getYacht();
if (GameState.getCurrentStage() == GameStages.PRE_RACE){
boatStatus = BoatStatus.PRESTART;
}
else if(GameState.getCurrentStage() == GameStages.RACING){
boatStatus = BoatStatus.RACING;
} else {
boatStatus = BoatStatus.UNDEFINED;
}
BoatSubMessage m = new BoatSubMessage(y.getSourceId(), boatStatus, 0, 0, 0, 1234l, 1234l);
boatSubMessages.add(m);
}
if (GameState.getCurrentStage() == GameStages.RACING){
raceStatus = RaceStatus.STARTED;
} else {
raceStatus = RaceStatus.WARNING;
}
sendMessage(new RaceStatusMessage(1, raceStatus, startTime, WindDirection.SOUTH,
100, GameState.getPlayers().size(), RaceType.MATCH_RACE, 1, boatSubMessages));
}
} }
+1 -1
View File
@@ -38,7 +38,7 @@
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints> </rowConstraints>
<children> <children>
<Button focusTraversable="false" mnemonicParsing="false" onAction="#readyButtonPressed" prefWidth="101.0" text="Ready" GridPane.halignment="CENTER" /> <Button fx:id="readyButton" focusTraversable="false" mnemonicParsing="false" onAction="#readyButtonPressed" prefWidth="101.0" text="Ready" GridPane.halignment="CENTER" />
<Button focusTraversable="false" mnemonicParsing="false" onAction="#leaveLobbyButtonPressed" text="Leave Lobby" GridPane.columnIndex="1" GridPane.halignment="CENTER" /> <Button focusTraversable="false" mnemonicParsing="false" onAction="#leaveLobbyButtonPressed" text="Leave Lobby" GridPane.columnIndex="1" GridPane.halignment="CENTER" />
</children> </children>
</GridPane> </GridPane>