package net.sf.colossus.server;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Logger;
import net.sf.colossus.common.Constants;
import net.sf.colossus.util.Glob;
public class SuspendGameRequest
{
private static final Logger LOGGER = Logger
.getLogger(SuspendGameRequest.class.getName());
private final Server server;
private final HashSet<ClientHandler> suspendRequestApprovers = new HashSet<ClientHandler>();
private final List<String> denyingClients = new LinkedList<String>();
private ClientHandler currentRequestor;
private Timer timer;
public SuspendGameRequest(Server server)
{
this.server = server;
}
public void requestToSuspendGame()
{
synchronized (suspendRequestApprovers)
{
sendApprovalRequest();
}
}
private void sendApprovalRequest()
{
currentRequestor = server.processingCH;
String playerName = currentRequestor.getPlayerName();
LOGGER.info("Player " + playerName + " requests to suspend the game.");
if (!suspendRequestApprovers.isEmpty())
{
LOGGER.warning("Player " + playerName + " requested to suspend "
+ "the game, but a previous request is still ongoing!");
server.processingCH.nak("Another request is still in progress.",
"Suspend request ignored");
return;
}
denyingClients.clear();
suspendRequestApprovers.clear();
timer = setupTimer();
// collect all eligible ones to request approval; excludes the stub
for (ClientHandler client : server.getRealClients())
{
if (client.equals(server.processingCH))
{
LOGGER.finest("Skipping requesting CH "
+ client.getPlayerName());
}
else if (client.isSpectator())
{
LOGGER.finest("Skipping spectator CH "
+ client.getPlayerName());
}
else if (client.canHandleSuspendRequests())
{
LOGGER.finest("A client to be asked: "
+ client.getPlayerName());
suspendRequestApprovers.add(client);
}
else
{
LOGGER.finest("Client can't handle the request: "
+ client.getPlayerName());
}
}
if (suspendRequestApprovers.size() == 0)
{
LOGGER.finest("No other clients can confirm suspend; asking the "
+ "requestor itself to have a client message to trigger the "
+ "suspend handling");
suspendRequestApprovers.add(server.processingCH);
}
else
{
LOGGER.finest("There are " + suspendRequestApprovers.size()
+ " (other) clients that can handle that request");
}
List<ClientHandler> approvers = new LinkedList<ClientHandler>(
(suspendRequestApprovers));
for (ClientHandler client : approvers)
{
LOGGER.finest("Sending suspend request to player "
+ client.getPlayerName());
client.askSuspendConfirmation(playerName,
Constants.SUSPEND_APPROVE_TIMEOUT);
}
}
public void handleOneResponse(boolean approved)
{
if (suspendRequestApprovers.isEmpty())
{
// silently ignore replies that come "too late"
return;
}
LOGGER.info("Player " + server.processingCH + " replies: " + approved);
synchronized (suspendRequestApprovers)
{
suspendRequestApprovers.remove(server.processingCH);
if (!approved)
{
denyingClients.add(server.processingCH.getPlayerName());
}
if (suspendRequestApprovers.isEmpty())
{
timer.cancel();
timer = null;
handleAllResponsedReceived(false);
}
}
}
private void handleAllResponsedReceived(boolean timedOut)
{
if (suspendRequestApprovers.isEmpty() || timedOut)
{
LOGGER.info("Handling the responses");
if (denyingClients.size() == 0)
{
LOGGER.info((timedOut ? "Timed out" : "All approved")
+ ": suspending game...");
server.getGame().getNotifyWebServer().gameIsSuspended();
server.initiateSuspendGame();
}
else
{
suspendDenied();
}
currentRequestor = null;
}
else
{
int count = suspendRequestApprovers.size();
LOGGER.finest("Waiting for " + count + " more responses");
}
}
public void suspendDenied()
{
int count = denyingClients.size();
String playerList = Glob.glob(", ", denyingClients);
String message = "Player " + currentRequestor.getPlayerName()
+ " had requested to suspend the game, but " + count + " player"
+ (count == 1 ? "" : "s") + " (" + playerList
+ ") denied it. Game will not be suspended.";
LOGGER.info("Informing clients: " + message);
server.messageFromServerToAll(message);
}
private Timer setupTimer()
{
// java.util.Timer, not Swing Timer
timer = new Timer("SuspendResponseTimeout-timer", true);
long timeout = 10; // secs
timer.schedule(new TriggerTimeIsUp(), timeout * 1000);
return timer;
}
class TriggerTimeIsUp extends TimerTask
{
boolean cancelled = false;
@Override
public void run()
{
synchronized (suspendRequestApprovers)
{
if (cancelled || suspendRequestApprovers.isEmpty())
{
// Nothing to do any more
}
else
{
handleAllResponsedReceived(true);
}
suspendRequestApprovers.notify();
cancel();
timer = null;
}
}
@Override
public boolean cancel()
{
cancelled = true;
return true;
}
}
}