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
@@ -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() {
for (ServerToClientThread serverToClientThread : serverToClientThreads) {
serverToClientThread.updateClient();
@@ -146,4 +143,9 @@ public class MainServerThread extends Observable implements Runnable, PacketBuff
// sendXml();
}
public void startGame() {
for (ServerToClientThread serverToClientThread : serverToClientThreads) {
serverToClientThread.sendRaceStatusMessage();
}
}
}
@@ -8,6 +8,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import java.util.Random;
@@ -23,12 +24,18 @@ import seng302.models.xml.Regatta;
import seng302.models.xml.XMLGenerator;
import seng302.server.messages.BoatActionType;
import seng302.server.messages.BoatLocationMessage;
import seng302.server.messages.BoatStatus;
import seng302.server.messages.BoatSubMessage;
import seng302.server.messages.Message;
import java.io.*;
import java.net.Socket;
import java.util.zip.CRC32;
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.XMLMessageSubType;
import seng302.server.messages.XMLMessage;
@@ -172,6 +179,8 @@ public class ServerToClientThread implements Runnable, Observer {
// message = new XMLMessage(xml, XMLMessageSubType.BOAT, 0);
// sendMessage(message);
// System.out.println("[server] send message 4 " + message);
// sendMessage(getRaceStatusMessage());
// System.out.println("sent race status");
//-------
while(true) {
@@ -215,7 +224,7 @@ public class ServerToClientThread implements Runnable, Observer {
break;
}
} else {
System.err.println("Packet has been dropped");
serverLog("Packet has been dropped", 1);
}
}
} catch (Exception e) {
@@ -359,10 +368,42 @@ public class ServerToClientThread implements Runnable, Observer {
}
}
public Thread getThread() {
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));
}
}