Merge remote-tracking branch 'origin/1047_Hosting_Game' into 1047_Hosting_Game

# Conflicts:
#	src/main/java/seng302/gameServer/GameServerThread.java
This commit is contained in:
William Muir
2017-07-13 22:55:49 +12:00
6 changed files with 174 additions and 86 deletions
@@ -3,5 +3,15 @@ package seng302.gameServer;
import seng302.models.Player;
public interface ClientConnectionDelegate {
/**
* A player has connected to the server
* @param player The player that has connected
*/
void clientConnected(Player player);
/**
* A player has disconnected from the server
* @param player The player that has disconnected
*/
void clientDisconnected(Player player);
}
@@ -3,10 +3,14 @@ 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.*;
@@ -22,7 +26,6 @@ public class GameServerThread implements Runnable, Observer, ClientConnectionDel
private long startTime;
private short seqNum;
private final int HEARTBEAT_PERIOD = 5000;
private final int RACE_STATUS_PERIOD = 1000/2;
private final int RACE_START_STATUS_PERIOD = 1000;
private final int BOAT_LOCATION_PERIOD = 1000/5;
@@ -124,28 +127,6 @@ public class GameServerThread implements Runnable, Observer, ClientConnectionDel
100, GameState.getPlayers().size(), RaceType.MATCH_RACE, 1, boatSubMessages);
}
/**
* Starts sending heartbeat messages to the client
*/
private void startSendingHeartbeats() {
Timer t = new Timer();
t.schedule(new TimerTask() {
@Override
public void run() {
Message heartbeat = new Heartbeat(seqNum);
try {
System.out.println("Sending heartbeat");
broadcast(heartbeat);
} catch (IOException e) {
e.printStackTrace();
}
}
}, 0, HEARTBEAT_PERIOD);
}
/**
* Start sending race start status messages until race starts
*/
@@ -160,7 +141,6 @@ public class GameServerThread implements Runnable, Observer, ClientConnectionDel
if (startTime < System.currentTimeMillis() && GameState.getCurrentStage() != GameStages.RACING){
}
else{
System.out.println("Sending race start status");
broadcast(raceStartStatusMessage);
}
@@ -200,15 +180,12 @@ public class GameServerThread implements Runnable, Observer, ClientConnectionDel
Message regatta = getXmlMessage("/server_config/regatta.xml", XMLMessageSubType.REGATTA);
if (raceData != null){
System.out.println("Sending RaceXML");
broadcast(raceData);
}
if (boatData != null){
System.out.println("Sending boatsXML");
broadcast(boatData);
}
if (regatta != null){
System.out.println("Sending regattaXML");
broadcast(regatta);
}
} catch (IOException e) {
@@ -227,7 +204,6 @@ public class GameServerThread implements Runnable, Observer, ClientConnectionDel
try {
Message raceData = getXmlMessage("/server_config/courseLimits.xml", XMLMessageSubType.RACE);
if (raceData != null) {
System.out.println("Sending courseLimitsXML");
broadcast(raceData);
}
}catch (IOException e) {
@@ -240,12 +216,17 @@ public class GameServerThread implements Runnable, Observer, ClientConnectionDel
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){
@@ -262,7 +243,6 @@ public class GameServerThread implements Runnable, Observer, ClientConnectionDel
if (GameState.getCurrentStage() == GameStages.RACING && !serverIsSendingMessages) {
serverLog("Race Started", 0);
startSendingHeartbeats();
sendXml();
startSendingRaceStartStatusMessages();
//startSendingRaceStatusMessages();
@@ -275,8 +255,7 @@ public class GameServerThread implements Runnable, Observer, ClientConnectionDel
}
startTime = System.currentTimeMillis() + TIME_TILL_RACE_START;
}
}
}
// /**
@@ -310,44 +289,35 @@ public class GameServerThread implements Runnable, Observer, ClientConnectionDel
@Override
public void clientConnected(Player player) {
if (GameState.getPlayers().size() < MAX_NUM_PLAYERS && GameState.getCurrentStage() == GameStages.LOBBYING) {
System.out.println("");
serverLog("Player Connected", 0);
GameState.addPlayer(player);
sendXml();
}
sendXml();
}
/**
* Listens for a connection and upon finding one, creates a Player object and adds it to the universal GameState
* A player has left the game, remove the player from the GameState
* @param player The player that left
*/
private void acceptConnection() {
try {
SocketChannel thisClient = server.accept();
if (thisClient.socket() != null){
Player thisPlayer = new Player(thisClient);
GameState.addPlayer(thisPlayer);
}
} catch (IOException e) {
e.getMessage();
}
@Override
public void clientDisconnected(Player player) {
serverLog("Player disconnected", 0);
GameState.removePlayer(player);
sendXml();
}
void unicast(Message message, SocketChannel client) throws IOException {
message.send(client); // TODO: 11/07/17 Do we incement seqNum for individual messages?
message.send(client);
}
void broadcast(Message message) throws IOException{
for(Player player : GameState.getPlayers()) {
System.out.println("Sending message seqNo[" + seqNum + "] to Player: " + player.toString());
//System.out.println("Sending message seqNo[" + seqNum + "] to Player: " + player.toString());
message.send(player.getSocketChannel());
}
seqNum++; // TODO: 11/07/17 Do we increment seqNum for every message or for the one message to everyone
seqNum++;
}
/**
* Send a boat location message when they are updated by the simulator
* @param o .
@@ -356,42 +326,39 @@ public class GameServerThread implements Runnable, Observer, ClientConnectionDel
@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();
// }
// }
/* 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 {
@@ -0,0 +1,81 @@
package seng302.gameServer;
import seng302.models.Player;
import seng302.server.messages.Heartbeat;
import seng302.server.messages.Message;
import java.io.IOException;
import java.util.*;
/**
* Send Heartbeat messages to connected player at a specified interval
* Will call .clientDisconnected on the delegate when a heartbeat message
* cannot be sent to a player
*/
public class HeartbeatThread extends Thread{
private final int HEARTBEAT_PERIOD = 5000;
private ClientConnectionDelegate delegate;
private Integer seqNum;
private Stack<Player> disconnectedPlayers;
HeartbeatThread(ClientConnectionDelegate delegate){
this.delegate = delegate;
seqNum = 0;
disconnectedPlayers = new Stack<>();
}
/**
* A player has lost connection to the server
* The player is added to a stack so that the delegate
* can be notified
*
* @param player The player that has disconnected
*/
private void playerLostConnection(Player player){
disconnectedPlayers.push(player);
}
/**
* Sends a heartbeat message to each connected player
* The delegate is notified if a player has disconnected
*/
private void sendHeartbeatToAllPlayers(){
Message heartbeat = new Heartbeat(seqNum);
for (Player player : GameState.getPlayers()){
if (!player.getSocketChannel().isConnected()){
playerLostConnection(player);
}
try {
heartbeat.send(player.getSocketChannel());
} catch (IOException e) {
playerLostConnection(player);
}
}
updateDelegate();
seqNum++;
}
/**
* Notifies the delegate about
* each disconnected player
*/
private void updateDelegate() {
while (!disconnectedPlayers.empty()){
delegate.clientDisconnected(disconnectedPlayers.pop());
}
}
public void run(){
Timer t = new Timer();
t.schedule(new TimerTask() {
@Override
public void run() {
sendHeartbeatToAllPlayers();
}
}, 0, HEARTBEAT_PERIOD);
}
}
@@ -1,6 +1,5 @@
package seng302.gameServer;
import com.sun.corba.se.spi.activation.Server;
import seng302.models.Player;
import java.io.IOException;
@@ -15,7 +14,7 @@ public class ServerListenThread extends Thread{
private ServerSocketChannel socketChannel;
private ClientConnectionDelegate delegate;
public ServerListenThread(ServerSocketChannel socketChannel, ClientConnectionDelegate delegate){
ServerListenThread(ServerSocketChannel socketChannel, ClientConnectionDelegate delegate){
this.socketChannel = socketChannel;
this.delegate = delegate;
}
+23 -1
View File
@@ -24,7 +24,6 @@ public class Player {
return socketChannel;
}
public Integer getLastMarkPassed() {
return lastMarkPassed;
}
@@ -40,6 +39,11 @@ public class Player {
@Override
public String toString() {
String playerAddress = null;
if (socketChannel == null){
return "Disconnected Player";
}
try {
playerAddress = socketChannel.getRemoteAddress().toString();
} catch (IOException e) {
@@ -48,4 +52,22 @@ public class Player {
return playerAddress;
}
@Override
public boolean equals(Object obj) {
if (obj == null){
return false;
}
if (!(obj instanceof Player)){
return false;
}
return ((Player) obj).socketChannel.equals(socketChannel);
}
@Override
public int hashCode(){
return socketChannel.hashCode();
}
}
@@ -54,6 +54,15 @@ public class RaceStartStatusMessage extends Message {
writeCRC();
rewind();
outputStream.write(getBuffer());
if (outputStream == null){
return;
}
try{
outputStream.write(getBuffer());
}
catch (IOException e){
return;
}
}
}