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/1047_Hosting_Game' into 1047_Hosting_Game
# Conflicts: # src/main/java/seng302/gameServer/GameServerThread.java
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user