package net.sf.colossus.client;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.sf.colossus.common.Constants;
import net.sf.colossus.game.BattlePhase;
import net.sf.colossus.game.EntrySide;
import net.sf.colossus.game.Legion;
import net.sf.colossus.game.Player;
import net.sf.colossus.game.PlayerColor;
import net.sf.colossus.util.Glob;
import net.sf.colossus.util.Split;
import net.sf.colossus.variant.BattleHex;
import net.sf.colossus.variant.CreatureType;
import net.sf.colossus.variant.MasterHex;
/**
* This will be a thread that executes all the client side processing;
* additional to the actual SocketClientThread which did that earlier.
*
* SCT will only handle the reading from Socket and push incoming
* messages to a queue and return back to listening on the socket.
* This way, it should be able to read all data in time so that no
* data pile up, and reply to ping requests almost instantly.
*
* ClientThread gets most of messages to parse already via a queue.
* Should also sending use a queue, and/or, the sending methods be
* inside ClientThread, and SocketClientThread merely provide the
* sendToServer method (which could be implemented differently e.g.
* in a "send via queue, instead of via socket", for local clients)?
*
* @author Clemens Katzer
*/
public class ClientThread extends Thread implements EventExecutor
{
private static final Logger LOGGER = Logger.getLogger(ClientThread.class
.getName());
private static int threadNumberCounter = 0;
private final int threadNr;
private final Client client;
private final LinkedBlockingQueue<ServerEvent> queue;
private boolean done = false;
private final boolean _DEBUG_MSGS = false;
// if we enable that, things get stuck... (perhaps because logger
// of all threads need then to get a lock on the logger too often?)
private final boolean LOG_PROCESSING_TIMES = false;
// this is enqueued to get the thread out of the "take()"-waiting
private final static ServerEvent END_EVENT = new ClientThread.ServerEvent(
0L, "END", new ArrayList<String>());
public ClientThread(Client client)
{
this.client = client;
this.threadNr = nextThreadNumber();
queue = new LinkedBlockingQueue<ServerEvent>();
}
private static synchronized int nextThreadNumber()
{
return ++threadNumberCounter;
}
public int getThreadNumber()
{
return this.threadNr;
}
public void enqueue(String method, List<String> args)
{
queue.offer(new ServerEvent(ClientThread.getNow(), method, args));
}
// Client checks whether still something to do from "here" before
// switching to new connectio (after reconnect)
public int getQueueLen()
{
return queue.size();
}
/**
* Take the method names of all events currently in the queue, and return
* them as a String, separated by comma.
* @return String with list of method names, comma-separated
*/
public String getQueueContentSummary()
{
Object items[] = queue.toArray();
List<String> methodNameList = new LinkedList<String>();
for (Object item : items)
{
if (item instanceof ServerEvent)
{
ServerEvent event = (ServerEvent)item;
methodNameList.add(event.getMethod());
}
else
{
methodNameList.add("<???>");
}
}
return Glob.glob(", ", methodNameList);
}
public void disposeQueue()
{
// Get thread out of it's "take" waiting
done = true;
boolean success = queue.offer(END_EVENT);
if (!success)
{
LOGGER.warning("CT " + getName()
+ ": failed to offer END signal to queue!");
}
}
public boolean isEngagementStartupOngoing()
{
return client.isEngagementStartupOngoing();
}
public void clearEngagementStartupOngoing()
{
client.setEngagementStartupOngoing(false);
}
public void disposeClient()
{
client.disposeClient();
}
public void setClosedByServer()
{
client.setClosedByServer();
}
public String getNameMaybe()
{
if (client != null && client.getOwningPlayer() != null)
{
return client.getOwningPlayer().getName();
}
else
{
return "<no client or no owning player name yet>";
}
}
private ServerEvent lastInactivityRelevantEvent;
private boolean retriggeredEventOngoing;
public void retriggerEventAsThread()
{
Runnable eventRunner = new Runnable()
{
public void run()
{
//
}
};
new Thread(eventRunner).start();
}
public void retriggerEvent()
{
if (lastInactivityRelevantEvent == null)
{
LOGGER
.severe("retriggerEvent called, but lastInactivityRelevantEvent is null ???");
return;
}
if (lastInactivityRelevantEvent.getIsRetriggered())
{
LOGGER
.finest("\n..., ah, but this is the retriggered one. Doing nothing.");
return;
}
try
{
lastInactivityRelevantEvent.setIsRetriggered();
ServerEvent tmp = lastInactivityRelevantEvent;
lastInactivityRelevantEvent = null;
queue.put(tmp);
}
catch (InterruptedException e)
{
LOGGER.severe("queue.put() interrupted?!?!");
}
}
public boolean isThereALastEvent()
{
return lastInactivityRelevantEvent != null;
}
public boolean getRetriggeredEventOngoing()
{
return retriggeredEventOngoing;
}
@Override
public void run()
{
LOGGER.finest("ClientThread run() started.");
while (!done)
{
ServerEvent event = null;
try
{
event = queue.take();
if (event == END_EVENT)
{
continue;
}
event.executionStarts(ClientThread.getNow());
retriggeredEventOngoing = event.isRetriggered;
callMethod(event);
retriggeredEventOngoing = false;
event.executionCompleted(ClientThread.getNow());
}
catch (InterruptedException e)
{
LOGGER.severe("queue.take() interrupted?!?!");
}
if (event != null)
{
if (LOG_PROCESSING_TIMES)
{
event.logProcessing();
}
}
else
{
LOGGER.severe("null event - bailed out with exception??");
}
}
LOGGER.finest("ClientThread run() ending.");
}
public void notifyUserIfGameIsPaused(String message)
{
if (client.isPaused())
{
LOGGER.info("Game in \"Pause\" state - "
+ "sending message anyway.");
if (!message.startsWith(Constants.replyToPing))
{
client.showMessageDialog("NOTE: Game is paused "
+ "- server will not confirm/react to any of\n"
+ "your GUI activities (move, split, ...), and thus "
+ "they will not show effect on the Board yet!");
}
}
}
public void notifyThatNotConnected()
{
client.notifyThatNotConnected();
}
public void appendToConnectionLog(String s)
{
client.appendToConnectionLog(s);
}
static int _MAXLEN = 80;
private void callMethod(ServerEvent event)
{
String method = event.getMethod();
List<String> args = event.getArgs();
if (client != null && !client.getOwningPlayer().isAI()
&& client.isFightPhase())
{
String allArgs = new String();
allArgs = Glob.glob(", ", args);
if (_MAXLEN != 0)
{
int len = allArgs.length();
if (len > _MAXLEN)
{
allArgs = allArgs.substring(0, _MAXLEN) + "...";
}
}
if (isEngagementStartupOngoing())
{
client.logMsgToServer("I", "CT, message: " + method + "("
+ allArgs + ")");
}
LOGGER.info("CT, message: " + method + "(" + allArgs + ")");
}
else
{
LOGGER.finest("Client (CT) '" + getNameMaybe()
+ "' processing message: " + method);
}
showDebugOutputMaybe(method, args);
if (method.equals(Constants.tellMovementRoll))
{
int roll = Integer.parseInt(args.remove(0));
String reason = "";
if (args.size() > 0)
{
reason = args.remove(0);
}
LOGGER.finest("tellMovementRoll, roll=" + roll + ", reason='"
+ reason + ".");
client.tellMovementRoll(roll, reason);
}
else if (method.equals(Constants.syncOption))
{
String optname = args.remove(0);
String value = args.remove(0);
client.syncOption(optname, value);
}
else if (method.equals(Constants.updatePlayerInfo))
{
List<String> infoStrings = Split.split(Glob.sep, args.remove(0));
client.updatePlayerInfo(infoStrings);
}
else if (method.equals(Constants.updateChangedValues))
{
String valuesString = args.remove(0);
String reason = args.remove(0);
client.updateChangedPlayerValues(valuesString, reason);
}
else if (method.equals(Constants.setColor))
{
String colorName = args.remove(0);
client.setColor(PlayerColor.getByName(colorName));
}
else if (method.equals(Constants.updateCreatureCount))
{
String creatureName = args.remove(0);
int count = Integer.parseInt(args.remove(0));
int deadCount = Integer.parseInt(args.remove(0));
client.updateCreatureCount(resolveCreatureType(creatureName),
count, deadCount);
}
else if (method.equals(Constants.removeLegion))
{
String id = args.remove(0);
client.removeLegion(resolveLegion(id));
}
else if (method.equals(Constants.setLegionStatus))
{
String markerId = args.remove(0);
boolean moved = Boolean.valueOf(args.remove(0)).booleanValue();
boolean teleported = Boolean.valueOf(args.remove(0))
.booleanValue();
int entrySideId = Integer.parseInt(args.remove(0));
String lastRecruit = args.remove(0);
client.setLegionStatus(resolveLegion(markerId), moved, teleported,
EntrySide.values()[entrySideId],
resolveCreatureType(lastRecruit));
}
else if (method.equals(Constants.addCreature))
{
String markerId = args.remove(0);
String name = args.remove(0);
String reason = args.isEmpty() ? "<Unknown>" : args.remove(0);
client.addCreature(resolveLegion(markerId),
resolveCreatureType(name), reason);
}
else if (method.equals(Constants.removeCreature))
{
String markerId = args.remove(0);
String name = args.remove(0);
String reason = args.isEmpty() ? "<Unknown>" : args.remove(0);
client.removeCreature(resolveLegion(markerId),
resolveCreatureType(name), reason);
}
else if (method.equals(Constants.revealCreatures))
{
String markerId = args.remove(0);
String namesString = args.remove(0);
List<String> names = Split.split(Glob.sep, namesString);
// safeguard against getting empty string list from server
// TODO: should split be fixed instead??
if (namesString.equals("") && names.size() > 0
&& names.get(0).equals(""))
{
names.remove(0);
}
String reason = args.isEmpty() ? "<Unknown>" : args.remove(0);
Player player = client.getGameClientSide().getPlayerByMarkerId(
markerId);
Legion legion;
if (player.hasLegion(markerId))
{
legion = player.getLegionByMarkerId(markerId);
}
else
{
// this can happen on game startup since there is no explicit
// event creating the first legions
// TODO try to make this less implicit
assert client.getTurnNumber() == -1 : "Implicit legion creation should happen only "
+ "before the first round";
legion = new LegionClientSide(player, markerId,
player.getStartingTower());
player.addLegion(legion);
}
List<CreatureType> creatures = new ArrayList<CreatureType>();
for (String name : names)
{
creatures.add(resolveCreatureType(name));
}
client.revealCreatures(legion, creatures, reason);
}
else if (method.equals(Constants.revealEngagedCreatures))
{
String markerId = args.remove(0);
boolean isAttacker = Boolean.valueOf(args.remove(0))
.booleanValue();
String names = args.remove(0);
String reason = args.isEmpty() ? "<Unknown>" : args.remove(0);
client.revealEngagedCreatures(resolveLegion(markerId),
resolveCreatureTypes(names), isAttacker, reason);
}
else if (method.equals(Constants.removeDeadBattleChits))
{
client.removeDeadBattleChits();
}
else if (method.equals(Constants.placeNewChit))
{
clearEngagementStartupOngoing();
String imageName = args.remove(0);
boolean inverted = Boolean.valueOf(args.remove(0)).booleanValue();
int tag = Integer.parseInt(args.remove(0));
String hexLabel = args.remove(0);
BattleHex hex = resolveBattleHex(hexLabel);
client.placeNewChit(imageName, inverted, tag, hex);
}
else if (method.equals(Constants.replayOngoing))
{
boolean val = Boolean.valueOf(args.remove(0)).booleanValue();
// older servers may not send this...
// TODO obsolete... nowadays they do, and there are other,
// incompatiblities added since then...
String turnArgMaybe = args.isEmpty() ? "0" : args.remove(0);
int maxTurn = Integer.parseInt(turnArgMaybe);
client.tellReplay(val, maxTurn);
}
else if (method.equals(Constants.redoOngoing))
{
boolean val = Boolean.valueOf(args.remove(0)).booleanValue();
client.tellRedo(val);
}
else if (method.equals(Constants.initBoard))
{
client.initBoard();
client.setEventExecutor(this);
}
else if (method.equals(Constants.setPlayerName))
{
String playerName = args.remove(0);
client.setPlayerName(playerName);
}
else if (method.equals(Constants.createSummonAngel))
{
rememberEvent(event);
String markerId = args.remove(0);
client.createSummonAngel(resolveLegion(markerId));
}
else if (method.equals(Constants.askAcquireAngel))
{
rememberEvent(event);
String markerId = args.remove(0);
List<CreatureType> recruits = resolveCreatureTypes(args.remove(0));
client.askAcquireAngel(resolveLegion(markerId), recruits);
}
else if (method.equals(Constants.askChooseStrikePenalty))
{
rememberEvent(event);
List<String> choices = Split.split(Glob.sep, args.remove(0));
client.askChooseStrikePenalty(choices);
}
else if (method.equals(Constants.tellGameOver))
{
String message = args.remove(0);
boolean disposeFollows = false;
if (!args.isEmpty())
{
disposeFollows = Boolean.valueOf(args.remove(0))
.booleanValue();
}
boolean suspended = false;
if (!args.isEmpty())
{
suspended = Boolean.valueOf(args.remove(0)).booleanValue();
}
client.tellGameOver(message, disposeFollows, suspended);
}
else if (method.equals(Constants.tellPlayerElim))
{
String playerName = args.remove(0);
String slayerName = args.remove(0);
// TODO use the "noone" player instead of null if no slayer?
client.tellPlayerElim(
client.getPlayerByName(playerName),
slayerName.equals("null") ? null : (client.getGameClientSide()
.getPlayerByName(slayerName)));
}
else if (method.equals(Constants.askConcede))
{
rememberEvent(event);
String allyMarkerId = args.remove(0);
String enemyMarkerId = args.remove(0);
client.askConcede(resolveLegion(allyMarkerId),
resolveLegion(enemyMarkerId));
}
else if (method.equals(Constants.askFlee))
{
rememberEvent(event);
String allyMarkerId = args.remove(0);
String enemyMarkerId = args.remove(0);
client.askFlee(resolveLegion(allyMarkerId),
resolveLegion(enemyMarkerId));
}
else if (method.equals(Constants.askNegotiate))
{
rememberEvent(event);
String attackerId = args.remove(0);
String defenderId = args.remove(0);
client.askNegotiate(resolveLegion(attackerId),
resolveLegion(defenderId));
}
else if (method.equals(Constants.tellProposal))
{
String proposalString = args.remove(0);
client.tellProposal(proposalString);
}
else if (method.equals(Constants.tellSlowResults))
{
int targetTag = Integer.parseInt(args.remove(0));
int slowValue = Integer.parseInt(args.remove(0));
client.tellSlowResults(targetTag, slowValue);
}
else if (method.equals(Constants.tellStrikeResults))
{
int strikerTag = Integer.parseInt(args.remove(0));
int targetTag = Integer.parseInt(args.remove(0));
int strikeNumber = Integer.parseInt(args.remove(0));
List<String> rolls = Split.split(Glob.sep, args.remove(0));
int damage = Integer.parseInt(args.remove(0));
boolean killed = Boolean.valueOf(args.remove(0)).booleanValue();
boolean wasCarry = Boolean.valueOf(args.remove(0)).booleanValue();
int carryDamageLeft = Integer.parseInt(args.remove(0));
Set<String> carryTargetDescriptions = new HashSet<String>();
if (!args.isEmpty())
{
String buf = args.remove(0);
if (buf != null && buf.length() > 0)
{
List<String> ctdList = Split.split(Glob.sep, buf);
carryTargetDescriptions.addAll(ctdList);
}
}
client.tellStrikeResults(strikerTag, targetTag, strikeNumber,
rolls, damage, killed, wasCarry, carryDamageLeft,
carryTargetDescriptions);
}
else if (method.equals(Constants.initBattle))
{
String masterHexLabel = args.remove(0);
int battleTurnNumber = Integer.parseInt(args.remove(0));
String battleActivePlayerName = args.remove(0);
BattlePhase battlePhase = BattlePhase.values()[Integer
.parseInt(args.remove(0))];
String attackerMarkerId = args.remove(0);
String defenderMarkerId = args.remove(0);
client.initBattle(resolveHex(masterHexLabel), battleTurnNumber,
client.getPlayerByName(battleActivePlayerName), battlePhase,
resolveLegion(attackerMarkerId),
resolveLegion(defenderMarkerId));
}
else if (method.equals(Constants.cleanupBattle))
{
client.cleanupBattle();
}
else if (method.equals(Constants.nextEngagement))
{
rememberEvent(event);
client.nextEngagement();
}
else if (method.equals(Constants.doReinforce))
{
rememberEvent(event);
String markerId = args.remove(0);
client.doReinforce(resolveLegion(markerId));
}
else if (method.equals(Constants.didRecruit))
{
String markerId = args.remove(0);
String recruitName = args.remove(0);
String recruiterName = args.remove(0);
int numRecruiters = Integer.parseInt(args.remove(0));
client.didRecruit(resolveLegion(markerId),
resolveCreatureType(recruitName),
resolveCreatureType(recruiterName), numRecruiters);
}
else if (method.equals(Constants.undidRecruit))
{
String markerId = args.remove(0);
String recruitName = args.remove(0);
client.undidRecruit(resolveLegion(markerId),
resolveCreatureType(recruitName));
}
else if (method.equals(Constants.setupTurnState))
{
rememberEvent(event);
String activePlayerName = args.remove(0);
int turnNumber = Integer.parseInt(args.remove(0));
client.setupTurnState(client.getPlayerByName(activePlayerName),
turnNumber);
}
else if (method.equals(Constants.setupSplit))
{
String activePlayerName = args.remove(0);
int turnNumber = Integer.parseInt(args.remove(0));
client.setupSplit(client.getPlayerByName(activePlayerName),
turnNumber);
}
else if (method.equals(Constants.setupMove))
{
client.setupMove();
}
else if (method.equals(Constants.setupFight))
{
client.setupFight();
}
else if (method.equals(Constants.setupMuster))
{
client.setupMuster();
}
else if (method.equals(Constants.kickPhase))
{
client.kickPhase();
}
else if (method.equals(Constants.setupBattleSummon))
{
rememberEvent(event);
Player battleActivePlayer = client.getPlayerByName(args.remove(0));
int battleTurnNumber = Integer.parseInt(args.remove(0));
if (battleActivePlayer.equals(client.getOwningPlayer()))
{
rememberEvent(event);
}
client.setupBattleSummon(battleActivePlayer, battleTurnNumber);
}
else if (method.equals(Constants.setupBattleRecruit))
{
Player battleActivePlayer = client.getPlayerByName(args.remove(0));
int battleTurnNumber = Integer.parseInt(args.remove(0));
if (battleActivePlayer.equals(client.getOwningPlayer()))
{
rememberEvent(event);
}
client.setupBattleRecruit(battleActivePlayer, battleTurnNumber);
}
else if (method.equals(Constants.setupBattleMove))
{
Player battleActivePlayer = client.getPlayerByName(args.remove(0));
int battleTurnNumber = Integer.parseInt(args.remove(0));
client.setupBattleMove(battleActivePlayer, battleTurnNumber);
}
else if (method.equals(Constants.setupBattleFight))
{
BattlePhase battlePhase = BattlePhase.values()[Integer
.parseInt(args.remove(0))];
Player battleActivePlayer = client.getPlayerByName(args.remove(0));
if (battleActivePlayer.equals(client.getOwningPlayer()))
{
rememberEvent(event);
}
client.setupBattleFight(battlePhase, battleActivePlayer);
}
else if (method.equals(Constants.tellLegionLocation))
{
String markerId = args.remove(0);
String hexLabel = args.remove(0);
client.tellLegionLocation(resolveLegion(markerId),
resolveHex(hexLabel));
}
else if (method.equals(Constants.tellBattleMove))
{
int tag = Integer.parseInt(args.remove(0));
String startingHexLabel = args.remove(0);
String endingHexLabel = args.remove(0);
boolean undo = Boolean.valueOf(args.remove(0)).booleanValue();
BattleHex startingHex = resolveBattleHex(startingHexLabel);
BattleHex endingHex = resolveBattleHex(endingHexLabel);
client.tellBattleMove(tag, startingHex, endingHex, undo);
}
else if (method.equals(Constants.didMove))
{
String markerId = args.remove(0);
String startingHexLabel = args.remove(0);
String currentHexLabel = args.remove(0);
String entrySideLabel = args.remove(0);
boolean teleport = Boolean.valueOf(args.remove(0)).booleanValue();
// servers from older versions might not send this arg
String teleportingLord = null;
if (!args.isEmpty())
{
teleportingLord = args.remove(0);
if (teleportingLord.equals("null"))
{
teleportingLord = null;
}
}
boolean splitLegionHasForcedMove = false;
// servers from older versions might not send this arg
if (!args.isEmpty())
{
splitLegionHasForcedMove = Boolean.valueOf(args.remove(0))
.booleanValue();
}
client
.didMove(resolveLegion(markerId),
resolveHex(startingHexLabel), resolveHex(currentHexLabel),
EntrySide.fromLabel(entrySideLabel), teleport,
resolveCreatureType(teleportingLord),
splitLegionHasForcedMove);
}
else if (method.equals(Constants.undidMove))
{
String markerId = args.remove(0);
String formerHexLabel = args.remove(0);
String currentHexLabel = args.remove(0);
boolean splitLegionHasForcedMove = false;
// servers from older versions might not send this arg
if (!args.isEmpty())
{
splitLegionHasForcedMove = Boolean.valueOf(args.remove(0))
.booleanValue();
}
client.undidMove(resolveLegion(markerId),
resolveHex(formerHexLabel), resolveHex(currentHexLabel),
splitLegionHasForcedMove);
}
else if (method.equals(Constants.didSummon))
{
String summonerId = args.remove(0);
String donorId = args.remove(0);
String summon = args.remove(0);
client.didSummon(resolveLegion(summonerId),
resolveLegion(donorId), resolveCreatureType(summon));
}
else if (method.equals(Constants.undidSplit))
{
String splitoffId = args.remove(0);
String survivorId = args.remove(0);
int turn = Integer.parseInt(args.remove(0));
client.undidSplit(resolveLegion(splitoffId),
resolveLegion(survivorId), turn);
}
else if (method.equals(Constants.didSplit))
{
String hexLabel = args.remove(0);
String parentId = args.remove(0);
String childId = args.remove(0);
int childHeight = Integer.parseInt(args.remove(0));
List<CreatureType> splitoffs = resolveCreatureTypes(args.remove(0));
int turn = Integer.parseInt(args.remove(0));
// create client-side copy of new legion
MasterHex hex = resolveHex(hexLabel);
Legion parentLegion = resolveLegion(parentId);
Player player = parentLegion.getPlayer();
Legion newLegion = new LegionClientSide(player, childId, hex);
player.addLegion(newLegion);
client.didSplit(hex, parentLegion, newLegion, childHeight,
splitoffs, turn);
}
else if (method.equals(Constants.askPickColor))
{
rememberEvent(event);
List<String> clList = Split.split(Glob.sep, args.remove(0));
List<PlayerColor> colorsLeft = new ArrayList<PlayerColor>();
for (String colorName : clList)
{
colorsLeft.add(PlayerColor.getByName(colorName));
}
client.askPickColor(colorsLeft);
}
else if (method.equals(Constants.askPickFirstMarker))
{
rememberEvent(event);
client.askPickFirstMarker();
}
else if (method.equals(Constants.log))
{
if (!args.isEmpty())
{
String message = args.remove(0);
client.log(message);
}
}
else if (method.equals(Constants.nak))
{
String reason = args.remove(0);
String message = args.remove(0);
// NOTE: nak for SignOn is already handled in SCT at the moment...
client.nak(reason, message);
}
else if (method.equals(Constants.boardActive))
{
boolean val = Boolean.valueOf(args.remove(0)).booleanValue();
client.setBoardActive(val);
}
else if (method.equals(Constants.tellEngagement))
{
client.tellEngagement(resolveHex(args.remove(0)),
client.getLegion(args.remove(0)),
client.getLegion(args.remove(0)));
}
else if (method.equals(Constants.tellEngagementResults))
{
String winnerId = args.remove(0);
String resMethod = args.remove(0);
int points = Integer.parseInt(args.remove(0));
int turns = Integer.parseInt(args.remove(0));
Legion legion;
if (winnerId.equals("null"))
{
legion = null;
}
else
{
legion = resolveLegion(winnerId);
}
client.tellEngagementResults(legion, resMethod, points, turns);
}
else if (method.equals(Constants.tellWhatsHappening))
{
String message = args.remove(0);
client.tellWhatsHappening(message);
}
// a popup message
else if (method.equals(Constants.messageFromServer))
{
String message = args.remove(0);
client.messageFromServer(message);
}
// just written to log (which might become visible by itself if needed)
else if (method.equals(Constants.appendToConnectionLog))
{
String message = args.remove(0);
client.appendToConnectionLog(message);
}
else if (method.equals(Constants.syncCompleted))
{
int syncRequestNr = Integer.parseInt(args.remove(0));
client.tellSyncCompleted(syncRequestNr);
}
else if (method.equals(Constants.requestExtraRollApproval))
{
String requestorName = args.remove(0);
int requestId = Integer.parseInt(args.remove(0));
client.requestExtraRollApproval(requestorName, requestId);
}
else if (method.equals(Constants.askSuspendConfirmation))
{
String requestorName = args.remove(0);
int timeout = Integer.parseInt(args.remove(0));
client.askSuspendConfirmation(requestorName, timeout);
}
else if (method.equals(Constants.askConfirmCatchUp))
{
client.confirmWhenCaughtUp();
}
else if (method.equals(Constants.serverConnectionOK))
{
LOGGER.info("Received server connection OK message from server "
+ "for player " + getNameMaybe());
client.serverConfirmsConnection();
}
else if (method.equals(Constants.relayedPeerRequest))
{
String requestingClientName = args.remove(0);
client.relayedPeerRequest(requestingClientName);
}
else if (method.equals(Constants.relayBackReceivedMsg))
{
String respondingClientName = args.remove(0);
int queueLen = Integer.parseInt(args.remove(0));
LOGGER.info("In client " + getNameMaybe()
+ ", got back 'Received' message from client "
+ respondingClientName);
client.peerRequestReceivedBy(respondingClientName, queueLen);
}
else if (method.equals(Constants.relayBackProcessedMsg))
{
String respondingClientName = args.remove(0);
LOGGER.info("In client " + getNameMaybe()
+ ", got back 'Processed' message from client "
+ respondingClientName);
client.peerRequestProcessedBy(respondingClientName);
}
else
{
LOGGER.log(Level.SEVERE, "Bogus packet (Client, method: '"
+ method + "', args: " + args + ")");
}
LOGGER.finest("Client '" + getNameMaybe()
+ "' finished method processing");
}
private void showDebugOutputMaybe(String method, List<String> args)
{
if (!_DEBUG_MSGS)
{
return;
}
Player p = client.getOwningPlayer();
if (p == null)
{
LOGGER.warning("No owning player? Skipping debug output.");
return;
}
String allArgs = Glob.glob(", ", args);
boolean show = true;
if (method.startsWith("setOption") && allArgs.startsWith("ViewMode"))
{
// show this as the only one
}
else if (method.startsWith(Constants.updateCreatureCount)
// || method.startsWith(Constants.setLegionStatus)
// || method.startsWith(Constants.tellLegionLocation)
|| method.startsWith(Constants.serverConnectionOK)
|| method.startsWith(Constants.relayBackProcessedMsg)
|| method.startsWith(Constants.relayBackReceivedMsg)
// || method.startsWith(Constants.)
|| method.startsWith(Constants.pingRequest)
|| method.startsWith(Constants.syncOption) // setOption
)
{
show = false;
}
if (!show)
{
return;
}
String name = p.getName();
if (name.equals("remote") || name.equals("spectator"))
{
String indent = (client.isReplayOngoing() ? " " : "")
+ (client.isRedoOngoing() ? " " : "");
// System.out.println(indent + "!!!" + method + ":" + allArgs);
String printLine = allArgs;
int _MAXLEN = 20;
int len = allArgs.length();
if (len > _MAXLEN)
{
printLine = allArgs.substring(0, _MAXLEN) + "...";
}
if (method.startsWith(Constants.setupTurnState))
{
System.out.println("\n\n");
}
else if (client.getGame().getTurnNumber() >= 1)
{
// WhatNextManager.sleepFor(2000);
}
System.out.println(indent + "!!!" + method + ": " + printLine);
}
}
private void rememberEvent(ServerEvent event)
{
if (!client.needsWatchdog())
{
return;
}
String argsList = Glob.glob("::", event.getArgs());
if (event.getIsRetriggered())
{
LOGGER.finest("NOT Storing retriggered event "
+ event.getMethod() + "'" + argsList + "'");
}
else
{
LOGGER.finest("\n\nStoring event " + event.getMethod() + "'"
+ argsList + "'");
lastInactivityRelevantEvent = event;
}
}
private MasterHex resolveHex(String label)
{
MasterHex hexByLabel = client.getGame().getVariant().getMasterBoard()
.getHexByLabel(label);
assert hexByLabel != null : "Client got unknown hex label '" + label
+ "' from server";
return hexByLabel;
}
private BattleHex resolveBattleHex(String hexLabel)
{
return client.getGame().getBattleSite().getTerrain()
.getHexByLabel(hexLabel);
}
private List<CreatureType> resolveCreatureTypes(String nameList)
{
List<CreatureType> creatures = new ArrayList<CreatureType>();
if (!nameList.equals(""))
{
List<String> names = Split.split(Glob.sep, nameList);
for (String creatureName : names)
{
creatures.add(resolveCreatureType(creatureName));
}
}
return creatures;
}
private CreatureType resolveCreatureType(String creatureName)
{
if ((creatureName == null) || (creatureName.equals("null")))
{
return null;
}
CreatureType creatureByName = client.getGame().getVariant()
.getCreatureByName(creatureName);
assert creatureByName != null : "Client got unknown creature name '"
+ creatureName + "' from server";
return creatureByName;
}
private Legion resolveLegion(String markerId)
{
Legion legion = client.getLegion(markerId);
// it's not just "unknown" - it might also be at any point during
// the replay of a loaded game that there is no legion for that
// marker *at that particular moment*.
// Whereas delayed painting in EDT may cause similar error, they
// should be carefully distincted.
if (legion == null)
{
// If such a thing happens, there is something seriously wrong,
// so I don't see a use in continuing the game.
// This I call it severe, and log it always,
// not just an assertion.
LOGGER.severe("CT ResolveLegion for " + markerId + " in client "
+ getNameMaybe() + " gave null!");
}
// Peter made this assertion, I guess...
assert legion != null : "ClientThread.resolveLegion(" + markerId
+ " in client of player " + getNameMaybe() + " returned null!";
return legion;
}
public static long getNow()
{
return new Date().getTime();
}
public static class ServerEvent
{
private final long received;
private final long enqueued;
private long executionStarted;
private long executionCompleted;
private final String method;
private final List<String> args;
private boolean isRetriggered = false;
public ServerEvent(long received, String method, List<String> args)
{
this.received = received;
this.enqueued = ClientThread.getNow();
this.method = method;
this.args = new ArrayList<String>(args);
}
public String getMethod()
{
return method;
}
public void setIsRetriggered()
{
isRetriggered = true;
}
public boolean getIsRetriggered()
{
return isRetriggered;
}
/* Returns now a LinkedList, instead of the original list
* which was an ArrayList.
* We return a copy, so that processing the args does not
* leave an empty arg list, in case we need to retrigger it
* (too long inactivity).
* LinkedList suits actually better for processing, since
* the argument deserialisation use remove(0).
*/
public List<String> getArgs()
{
return new LinkedList<String>(args);
}
public void executionStarts(long when)
{
this.executionStarted = when;
}
public long getExecutionStarted()
{
return this.executionStarted;
}
public void executionCompleted(long when)
{
this.executionCompleted = when;
}
public long getExecutionCompleted()
{
return this.executionCompleted;
}
public void logProcessing()
{
long enqueuing = enqueued - received;
long execution = executionCompleted - executionStarted;
long inQueue = executionStarted - enqueued;
long processing = executionCompleted - received;
Level loglevel;
if (processing > 5000)
{
loglevel = Level.WARNING;
}
else
{
loglevel = Level.FINEST;
}
ClientThread thisThread = (ClientThread)Thread.currentThread();
LOGGER.log(loglevel, "Event " + method + " in thread #"
+ thisThread.getThreadNumber() + " received at " + received
+ ": overall processing took: " + processing
+ "ms (enqueuing took " + enqueuing + "ms, inQueue " + inQueue
+ "ms, processing took " + execution + "ms)");
}
}
}