package seng302.gameServer; 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; /** * 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 implements Runnable { private Logger logger = LoggerFactory.getLogger(HeartbeatThread.class); private final int HEARTBEAT_PERIOD = 200; private ClientConnectionDelegate delegate; private Integer seqNum; private Stack disconnectedPlayers; public HeartbeatThread(ClientConnectionDelegate delegate){ this.delegate = delegate; seqNum = 0; disconnectedPlayers = new Stack<>(); Thread thread = new Thread(this, "HeartBeat"); thread.start(); } /** * 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(){ 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"); } } /** * 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); } }