use of soc.server.genericServer.Connection in project JSettlers2 by jdmonin.
the class SOCGameHandler method forceEndGameTurn.
/**
* Try to force-end the current player's turn in this game.
* Alter game state and send messages to players.
* Will call {@link #endGameTurn(SOCGame, SOCPlayer, boolean)} if appropriate.
* Will send gameState and current player (turn) to clients.
*<P>
* If the current player has lost connection, send the {@link SOCLeaveGame LEAVEGAME}
* message out <b>before</b> calling this method.
*<P>
* Assumes, as {@link #endGameTurn(SOCGame, SOCPlayer, boolean)} does:
* <UL>
* <LI> ga.canEndTurn already called, returned false
* <LI> ga.takeMonitor already called (not the same as {@link SOCGameList#takeMonitorForGame(String)})
* <LI> gamelist.takeMonitorForGame is NOT called, we do NOT have that monitor
* </UL>
* @param ga Game to force end turn
* @param plName Current player's name. Needed because if they have been disconnected by
* {@link #leaveGame(SOCGame, Connection)},
* their name within game object is already null.
* @return true if the turn was ended and game is still active;
* false if we find that all players have left and
* the gamestate has been changed here to {@link SOCGame#OVER}.
*
* @see #endGameTurnOrForce(SOCGame, int, String, Connection, boolean)
* @see SOCGame#forceEndTurn()
*/
private final boolean forceEndGameTurn(SOCGame ga, final String plName) {
final String gaName = ga.getName();
final int cpn = ga.getCurrentPlayerNumber();
final int endFromGameState = ga.getGameState();
SOCPlayer cp = ga.getPlayer(cpn);
if (cp.hasAskedSpecialBuild()) {
cp.setAskedSpecialBuild(false);
srv.messageToGame(gaName, new SOCPlayerElement(gaName, cpn, SOCPlayerElement.SET, SOCPlayerElement.ASK_SPECIAL_BUILD, 0));
}
final SOCForceEndTurnResult res = ga.forceEndTurn();
// also could be initial placement (START1A or START2A or START3A).
if (SOCGame.OVER == ga.getGameState())
// <--- Early return: All players have left ---
return false;
/**
* Report any resources lost or gained.
* See also forceGamePlayerDiscardOrGain for same reporting code.
*/
SOCResourceSet resGainLoss = res.getResourcesGainedLost();
if (resGainLoss != null) {
/**
* If gold hex or returning resources to player (not discarding), report actual types/amounts.
* For discard, tell the discarding player's client that they discarded the resources,
* tell everyone else that the player discarded unknown resources.
*/
if (!res.isLoss()) {
if ((endFromGameState == SOCGame.WAITING_FOR_PICK_GOLD_RESOURCE) || (endFromGameState == SOCGame.STARTS_WAITING_FOR_PICK_GOLD_RESOURCE)) {
// Send SOCPlayerElement messages, "gains" text
reportRsrcGainGold(ga, cp, cpn, resGainLoss, true, false);
} else {
// Send SOCPlayerElement messages
reportRsrcGainLoss(gaName, resGainLoss, false, false, cpn, -1, null, null);
}
} else {
Connection c = srv.getConnection(plName);
if ((c != null) && c.isConnected())
reportRsrcGainLoss(gaName, resGainLoss, true, true, cpn, -1, null, c);
int totalRes = resGainLoss.getTotal();
srv.messageToGameExcept(gaName, c, new SOCPlayerElement(gaName, cpn, SOCPlayerElement.LOSE, SOCPlayerElement.UNKNOWN, totalRes, true), true);
// "{0} discarded {1} resources."
srv.messageToGameKeyed(ga, true, "action.discarded", plName, totalRes);
}
}
/**
* report any dev-card or item returned to player's hand
*/
final SOCInventoryItem itemCard = res.getReturnedInvItem();
SOCInventoryItemAction retItemActionMsg = null;
if (itemCard != null) {
Connection c = srv.getConnection(plName);
if ((c != null) && c.isConnected()) {
if (itemCard instanceof SOCDevCard) {
int card = itemCard.itype;
if ((card == SOCDevCardConstants.KNIGHT) && (c.getVersion() < SOCDevCardConstants.VERSION_FOR_NEW_TYPES))
card = SOCDevCardConstants.KNIGHT_FOR_VERS_1_X;
srv.messageToPlayer(c, new SOCDevCardAction(gaName, cpn, SOCDevCardAction.ADD_OLD, card));
} else {
retItemActionMsg = new SOCInventoryItemAction(gaName, cpn, (itemCard.isPlayable() ? SOCInventoryItemAction.ADD_PLAYABLE : SOCInventoryItemAction.ADD_OTHER), itemCard.itype, itemCard.isKept(), itemCard.isVPItem(), itemCard.canCancelPlay);
srv.messageToPlayer(c, retItemActionMsg);
}
}
// Announce item to game with same retItemActionMsg sent to player?
boolean announceAsInvItemAction = false;
// Announce this item to game as an unknown dev card type?
boolean announceAsUnknown = true;
if (!(itemCard instanceof SOCDevCard)) {
// SOCInventoryItem: Add any new kinds here, to announce to all players.
// If it needs a special message, do so and set announceAsUnknown = false
// If it's private and doesn't need a special message, set handled = true and let it announce as unknown
boolean handled = false;
if (ga.isGameOptionSet(SOCGameOption.K_SC_FTRI)) {
// endFromGameState is PLACING_INV_ITEM.
// "Gift port" item details are public, send return message to whole game:
handled = true;
announceAsInvItemAction = true;
announceAsUnknown = false;
}
// Fallback:
if (!handled)
System.err.println("forceEndGameTurn: Unhandled inventory item type " + itemCard.itype + " class " + itemCard.getClass());
}
if (announceAsInvItemAction) {
srv.messageToGameExcept(gaName, c, retItemActionMsg, true);
} else if (announceAsUnknown) {
if (ga.clientVersionLowest >= SOCDevCardConstants.VERSION_FOR_NEW_TYPES) {
srv.messageToGameExcept(gaName, c, new SOCDevCardAction(gaName, cpn, SOCDevCardAction.ADD_OLD, SOCDevCardConstants.UNKNOWN), true);
} else {
srv.messageToGameForVersionsExcept(ga, -1, SOCDevCardConstants.VERSION_FOR_NEW_TYPES - 1, c, new SOCDevCardAction(gaName, cpn, SOCDevCardAction.ADD_OLD, SOCDevCardConstants.UNKNOWN_FOR_VERS_1_X), true);
srv.messageToGameForVersionsExcept(ga, SOCDevCardConstants.VERSION_FOR_NEW_TYPES, Integer.MAX_VALUE, c, new SOCDevCardAction(gaName, cpn, SOCDevCardAction.ADD_OLD, SOCDevCardConstants.UNKNOWN), true);
}
srv.messageToGameKeyed(ga, true, "forceend.devcard.returned", plName);
// "{0}''s just-played development card was returned."
}
}
/**
* For initial placements, we don't end turns as normal.
* (Player number may go forward or backwards, new state isn't ROLL_OR_CARD, etc.)
* Update clients' gamestate, but don't call endGameTurn.
*/
final int forceRes = res.getResult();
if ((forceRes == SOCForceEndTurnResult.FORCE_ENDTURN_SKIP_START_ADV) || (forceRes == SOCForceEndTurnResult.FORCE_ENDTURN_SKIP_START_ADVBACK)) {
if (res.didUpdateFP() || res.didUpdateLP()) {
final int fpn = ga.getFirstPlayer();
final SOCMessage msg = (ga.clientVersionLowest >= SOCGameElements.MIN_VERSION) ? new SOCGameElements(gaName, SOCGameElements.FIRST_PLAYER, fpn) : new SOCFirstPlayer(gaName, fpn);
// will cause clients to recalculate lastPlayer too
srv.messageToGame(gaName, msg);
}
sendTurn(ga, false);
// <--- Early return ---
return true;
}
/**
* If the turn can now end, proceed as if player requested it.
* Otherwise, send current gamestate. We'll all wait for other
* players to send discard messages, and afterwards this turn can end.
*/
if (ga.canEndTurn(cpn))
// could force gamestate to OVER, if a client leaves
endGameTurn(ga, null, true);
else
sendGameState(ga, false, false);
return (ga.getGameState() != SOCGame.OVER);
}
use of soc.server.genericServer.Connection in project JSettlers2 by jdmonin.
the class SOCGameHandler method leaveGame.
// javadoc inherited from GameHandler. Return true if game is empty and should be ended.
public boolean leaveGame(SOCGame ga, Connection c) {
final String gm = ga.getName();
// Retain name, since will become null within game obj.
final String plName = c.getData();
boolean gameHasHumanPlayer = false;
boolean gameHasObserver = false;
@SuppressWarnings("unused") boolean // TODO checks/messages; added in v1.1.01, TODO not used yet
gameVotingActiveDuringStart = false;
final int gameState = ga.getGameState();
boolean isPlayer = false;
// removing this player number
int playerNumber;
for (playerNumber = 0; playerNumber < ga.maxPlayers; playerNumber++) {
SOCPlayer player = ga.getPlayer(playerNumber);
if ((player != null) && (player.getName() != null) && (player.getName().equals(plName))) {
isPlayer = true;
/**
* About to remove this player from the game. Before doing so:
* If a board-reset vote is in progress, they cannot vote
* once they have left. So to keep the game moving,
* fabricate their response: vote No.
*/
if (ga.getResetVoteActive()) {
if (gameState <= SOCGame.STARTS_WAITING_FOR_PICK_GOLD_RESOURCE)
gameVotingActiveDuringStart = true;
if (ga.getResetPlayerVote(playerNumber) == SOCGame.VOTE_NONE) {
srv.gameList.releaseMonitorForGame(gm);
ga.takeMonitor();
srv.resetBoardVoteNotifyOne(ga, playerNumber, plName, false);
ga.releaseMonitor();
srv.gameList.takeMonitorForGame(gm);
}
}
/**
* Remove the player.
*/
// player obj name becomes null
ga.removePlayer(plName);
// broadcastGameStats(cg);
break;
}
}
SOCLeaveGame leaveMessage = new SOCLeaveGame(plName, "-", gm);
srv.messageToGameWithMon(gm, leaveMessage);
srv.recordGameEvent(gm, leaveMessage);
if (D.ebugOn)
D.ebugPrintln("*** " + plName + " left the game " + gm + " at " + DateFormat.getTimeInstance(DateFormat.SHORT).format(new Date()));
// "{0} left the game"
srv.messageToGameKeyed(ga, false, "member.left.game", plName);
/**
* check if there is at least one person playing the game
*/
for (int pn = 0; pn < ga.maxPlayers; pn++) {
SOCPlayer player = ga.getPlayer(pn);
if ((player != null) && (player.getName() != null) && (!ga.isSeatVacant(pn)) && (!player.isRobot())) {
gameHasHumanPlayer = true;
break;
}
}
/**
* if no human players, check if there is at least one person watching the game (observing).
* Even with observers, end it unless ga.isBotsOnly or PROP_JSETTLERS_BOTS_BOTGAMES_TOTAL != 0.
*/
if ((!gameHasHumanPlayer) && !srv.gameList.isGameEmpty(gm)) {
Enumeration<Connection> membersEnum = srv.gameList.getMembers(gm).elements();
while (membersEnum.hasMoreElements()) {
Connection member = membersEnum.nextElement();
// D.ebugPrintln("*** "+member.data+" is a member of "+gm);
boolean nameMatch = false;
for (int pn = 0; pn < ga.maxPlayers; pn++) {
SOCPlayer player = ga.getPlayer(pn);
if ((player != null) && (player.getName() != null) && (player.getName().equals(member.getData()))) {
nameMatch = true;
break;
}
}
if (!nameMatch) {
gameHasObserver = true;
break;
}
}
if (gameHasObserver && !ga.isBotsOnly) {
if (0 == srv.getConfigIntProperty(SOCServer.PROP_JSETTLERS_BOTS_BOTGAMES_TOTAL, 0))
gameHasObserver = false;
}
}
/**
* if the leaving member was playing the game, and
* the game isn't over, then decide:
* - Do we need to force-end the current turn?
* - Do we need to cancel their initial settlement placement?
* - Should we replace the leaving player with a robot?
*/
if (isPlayer && (gameHasHumanPlayer || gameHasObserver) && ((ga.getPlayer(playerNumber).getPublicVP() > 0) || (gameState == SOCGame.START1A) || (gameState == SOCGame.START1B)) && (gameState < SOCGame.OVER) && !(gameState < SOCGame.START1A)) {
boolean foundNoRobots;
if (ga.getPlayer(playerNumber).isRobot()) {
/**
* don't replace bot with bot; force end-turn instead.
*/
foundNoRobots = true;
} else {
/**
* get a robot to replace this human player;
* just in case, check game-version vs robots-version,
* like at new-game (readyGameAskRobotsJoin).
*/
foundNoRobots = !findRobotAskJoinGame(ga, Integer.valueOf(playerNumber), true);
}
/**
* What to do if no robot was found to fill their spot?
* Must keep the game going, might need to force-end current turn.
*/
if (foundNoRobots) {
final boolean stillActive = endGameTurnOrForce(ga, playerNumber, plName, c, true);
if (!stillActive) {
// force game destruction below
gameHasHumanPlayer = false;
gameHasObserver = false;
}
}
} else {
// observer leaving: If game is bot-only, don't end the game despite no human players/observers
if (ga.isBotsOnly && (ga.getGameState() < SOCGame.OVER))
gameHasObserver = true;
}
return !(gameHasHumanPlayer || gameHasObserver);
}
use of soc.server.genericServer.Connection in project JSettlers2 by jdmonin.
the class SOCGameHandler method sendGameStateOVER.
/**
* If game is OVER, send messages reporting winner, final score,
* and each player's victory-point cards.
* Also give stats on game length, and on each player's connect time.
* If player has finished more than 1 game since connecting, send their win-loss count.
*<P>
* Increments server stats' numberOfGamesFinished.
* If db is active, calls {@link SOCServer#storeGameScores(SOCGame)} to save game stats.
*<P>
* If {@link SOCGame#isBotsOnly}, calls {@link SOCServer#destroyGameAndBroadcast(String, String)} to make room
* for more games to run: The bots don't know on their own to leave, it's easier for the
* server to dismiss them within {@code destroyGame}.
*<P>
* Make sure {@link SOCGameState}({@link SOCGame#OVER OVER}) is sent before calling this method.
*
* @param ga This game is over; state should be OVER
* @since 1.1.00
*/
private void sendGameStateOVER(SOCGame ga) {
final String gname = ga.getName();
/**
* Find and announce the winner
* (the real "found winner" code is in SOCGame.checkForWinner;
* that was already called before sendGameStateOVER.)
*/
SOCPlayer winPl = ga.getPlayer(ga.getCurrentPlayerNumber());
if ((winPl.getTotalVP() < ga.vp_winner) && !ga.hasScenarioWinCondition) {
// This is fallback code.
for (int i = 0; i < ga.maxPlayers; i++) {
if (winPl.getTotalVP() >= ga.vp_winner) {
winPl = ga.getPlayer(i);
break;
}
}
}
srv.messageToGameKeyed(ga, true, "stats.game.winner.withpoints", winPl.getName(), winPl.getTotalVP());
// "{0} has won the game with {1,number} points."
// / send a message with the revealed final scores
{
int[] scores = new int[ga.maxPlayers];
boolean[] isRobot = new boolean[ga.maxPlayers];
for (int i = 0; i < ga.maxPlayers; ++i) {
scores[i] = ga.getPlayer(i).getTotalVP();
isRobot[i] = ga.getPlayer(i).isRobot();
}
srv.messageToGame(gname, new SOCGameStats(gname, scores, isRobot));
}
// /
for (int i = 0; i < ga.maxPlayers; i++) {
SOCPlayer pl = ga.getPlayer(i);
List<SOCInventoryItem> vpCards = pl.getInventory().getByState(SOCInventory.KEPT);
if (!vpCards.isEmpty())
srv.messageToGameKeyedSpecial(ga, true, "endgame.player.has.vpcards", pl.getName(), vpCards);
// "Joe has a Gov.House (+1VP) and a Market (+1VP)" ["{0} has {1,dcards}."]
}
// for each player
/**
* send game-length and connect-length messages, possibly win-loss count.
*/
{
Date now = new Date();
Date gstart = ga.getStartTime();
if (gstart != null) {
final int gameRounds = ga.getRoundCount();
long gameSeconds = ((now.getTime() - gstart.getTime()) + 500L) / 1000L;
final long gameMinutes = gameSeconds / 60L;
gameSeconds = gameSeconds % 60L;
if (gameSeconds == 0)
srv.messageToGameKeyed(ga, true, "stats.game.was.roundsminutes", gameRounds, gameMinutes);
else
// "This game was # rounds, and took # minutes."
srv.messageToGameKeyed(ga, true, "stats.game.was.roundsminutessec", gameRounds, gameMinutes, gameSeconds);
// "This game was # rounds, and took # minutes # seconds." [or 1 second.]
// Ignore possible "1 minutes"; that game is too short to worry about.
}
/**
* Update each player's win-loss count for this session.
* Tell each player their resource roll totals.
* Tell each player how long they've been connected.
* (Robot players aren't told this, it's not necessary.)
*/
final String connMsgKey;
if (ga.isPractice)
// "You have been practicing # minutes."
connMsgKey = "stats.cli.connected.minutes.prac";
else
// "You have been connected # minutes."
connMsgKey = "stats.cli.connected.minutes";
for (int i = 0; i < ga.maxPlayers; i++) {
if (ga.isSeatVacant(i))
continue;
SOCPlayer pl = ga.getPlayer(i);
Connection plConn = srv.getConnection(pl.getName());
SOCClientData cd;
if (plConn != null) {
// Update win-loss count, even for robots
cd = (SOCClientData) plConn.getAppData();
if (pl == winPl)
cd.wonGame();
else
cd.lostGame();
} else {
// To satisfy compiler warning
cd = null;
}
if (pl.isRobot())
// <-- Don't bother to send any stats text to robots --
continue;
if (plConn != null) {
if (plConn.getVersion() >= SOCPlayerStats.VERSION_FOR_RES_ROLL) {
// Send total resources rolled
srv.messageToPlayer(plConn, new SOCPlayerStats(pl, SOCPlayerStats.STYPE_RES_ROLL));
}
final long connTime = plConn.getConnectTime().getTime();
final long connMinutes = (((now.getTime() - connTime)) + 30000L) / 60000L;
// "You have been connected # minutes."
srv.messageToPlayerKeyed(plConn, gname, connMsgKey, connMinutes);
// Send client's win-loss count for this session,
// if more than 1 game has been played
{
int wins = cd.getWins();
int losses = cd.getLosses();
if (wins + losses < 2)
// Only 1 game played so far
continue;
if (wins > 0) {
if (losses == 0)
srv.messageToPlayerKeyed(plConn, gname, "stats.cli.winloss.won", wins);
else
// "You have won {0,choice, 1#1 game|1<{0,number} games} since connecting."
srv.messageToPlayerKeyed(plConn, gname, "stats.cli.winloss.wonlost", wins, losses);
// "You have won {0,choice, 1#1 game|1<{0,number} games} and lost {1,choice, 1#1 game|1<{1,number} games} since connecting."
} else {
srv.messageToPlayerKeyed(plConn, gname, "stats.cli.winloss.lost", losses);
// "You have lost {0,choice, 1#1 game|1<{0,number} games} since connecting."
}
}
}
}
// for each player
}
// send game timing stats, win-loss stats
srv.gameOverIncrGamesFinishedCount();
srv.storeGameScores(ga);
if (ga.isBotsOnly) {
srv.destroyGameAndBroadcast(gname, "sendGameStateOVER");
}
// Server structure more or less ensures sendGameStateOVER is called only once.
// TODO consider refactor to be completely sure, especially for storeGameScores.
}
use of soc.server.genericServer.Connection in project JSettlers2 by jdmonin.
the class SOCServer method messageToGameForVersionsExcept.
/**
* Send a message to all the connections in a game in a certain version range, excluding one.
* Used for backwards compatibility.
*
* @param ga the game
* @param vmin Minimum version to send to, or -1. Same format as
* {@link Version#versionNumber()} and {@link Connection#getVersion()}.
* @param vmax Maximum version to send to, or {@link Integer#MAX_VALUE}
* @param ex the excluded connection, or null
* @param mes the message
* @param takeMon Should this method take and release
* game's monitor via {@link SOCGameList#takeMonitorForGame(String)} ?
* If the game's clients are all older than <tt>vmin</tt> or
* newer than <tt>vmax</tt>, nothing happens and the monitor isn't taken.
* @since 1.1.19
* @see #messageToGameExcept(String, Connection, SOCMessage, boolean)
*/
public final void messageToGameForVersionsExcept(final SOCGame ga, final int vmin, final int vmax, final Connection ex, final SOCMessage mes, final boolean takeMon) {
if ((ga.clientVersionLowest > vmax) || (ga.clientVersionHighest < vmin))
// <--- All clients too old or too new ---
return;
final String gn = ga.getName();
if (takeMon)
gameList.takeMonitorForGame(gn);
try {
Vector<Connection> v = gameList.getMembers(gn);
if (v != null) {
// lazy init, will be mes.toCmd()
String mesCmd = null;
Enumeration<Connection> menum = v.elements();
while (menum.hasMoreElements()) {
Connection con = menum.nextElement();
if ((con == null) || (con == ex))
continue;
final int cv = con.getVersion();
if ((cv < vmin) || (cv > vmax))
continue;
// currentGameEventRecord.addMessageOut(new SOCMessageRecord(mes, "SERVER", con.getData()));
if (mesCmd == null)
mesCmd = mes.toCmd();
con.put(mesCmd);
}
}
} catch (Exception e) {
D.ebugPrintStackTrace(e, "Exception in messageToGameForVersions");
}
if (takeMon)
gameList.releaseMonitorForGame(gn);
}
use of soc.server.genericServer.Connection in project JSettlers2 by jdmonin.
the class SOCServer method resetBoardAndNotify.
/**
* Reset the board, to a copy with same players but new layout.
* Here's the general outline; step 1 and 2 are done immediately here,
* steps 3 through n are done (after robots are dismissed) within
* {@link #resetBoardAndNotify_finish(SOCGameBoardReset, SOCGame)}.
*<OL>
* <LI value=1> Reset the board, remember player positions.
* If there are robots, set game state to
* {@link SOCGame#READY_RESET_WAIT_ROBOT_DISMISS}.
* <LI value=2a> Send ResetBoardAuth to each client (like sending JoinGameAuth at new game)
* Humans will reset their copy of the game.
* Robots will leave the game, and soon be requested to re-join.
* (This simplifies the robot client.)
* If the game was in initial placement or was already over at reset time, different robots will
* be randomly chosen to join the reset game.
* <LI value=2b> If there were robots, wait for them all to leave the old game.
* Otherwise, (race condition) they may leave the new game as it is forming.
* Set {@link SOCGame#boardResetOngoingInfo}.
* Wait for them to leave the old game before continuing.
* The call will be made from {@link SOCServerMessageHandler#handleLEAVEGAME_maybeGameReset_oldRobot(String)}.
* <LI value=2c> If no robots, immediately call {@link #resetBoardAndNotify_finish(SOCGameBoardReset, SOCGame)}.
* <P>
* <b>This ends this method.</b> Step 3 and the rest are in
* {@link #resetBoardAndNotify_finish(SOCGameBoardReset, SOCGame)}.
* <LI value=3> Send messages as if each human player has clicked "join" (except JoinGameAuth)
* <LI value=4> Send as if each human player has clicked "sit here"
* <LI value=5a> If no robots, send to game as if someone else has
* clicked "start game", and set up state to begin game play.
* <LI value=5b> If there are robots, set up wait-request
* queue (robotJoinRequests). Game will wait for robots to send
* JOINGAME and SITDOWN, as they do when joining a newly created game.
* Once all robots have re-joined, the game will begin.
*</OL>
* @since 1.1.00
*/
void resetBoardAndNotify(final String gaName, final int requestingPlayer) {
/**
* 1. Reset the board, remember player positions.
* Takes the monitorForGame and (when reset is ready) releases it.
* If robots, resetBoard will also set gamestate
* and boardResetOngoingInfo field.
*/
SOCGameBoardReset reBoard = gameList.resetBoard(gaName);
if (reBoard == null) {
final SOCGame ga = gameList.getGameData(gaName);
if (ga != null)
messageToGameKeyed(ga, true, "resetboard.doit.interror", gaName);
// <---- Early return: reset failed ----
return;
}
SOCGame reGame = reBoard.newGame;
// Announce who asked for this reset
{
String plName = reGame.getPlayer(requestingPlayer).getName();
final String key = (plName != null) ? // ">>> Game {0} board reset by {1}"
"resetboard.doit.announce.requester" : // ">>> Game {0} board reset by a player who left"
"resetboard.doit.announce.playerwholeft";
messageToGameKeyed(reGame, true, key, gaName, plName);
}
// If game is still initial-placing or was over, we'll shuffle the robots
final boolean resetWithShuffledBots = (reBoard.oldGameState < SOCGame.ROLL_OR_CARD) || (reBoard.oldGameState == SOCGame.OVER);
/**
* Player connection data:
* - Humans are copied from old to new game
* - Robots aren't copied to new game, must re-join
*/
Connection[] huConns = reBoard.humanConns;
Connection[] roConns = reBoard.robotConns;
/**
* Notify old game's players. (Humans and robots)
*
* 2a. Send ResetBoardAuth to each (like sending JoinGameAuth at new game).
* Humans will reset their copy of the game.
* Robots will leave the game, and soon will be requested to re-join.
*/
for (int pn = 0; pn < reGame.maxPlayers; ++pn) {
SOCResetBoardAuth resetMsg = new SOCResetBoardAuth(gaName, pn, requestingPlayer);
if (huConns[pn] != null)
messageToPlayer(huConns[pn], resetMsg);
else if (roConns[pn] != null) {
if (!resetWithShuffledBots)
// same robot will rejoin
messageToPlayer(roConns[pn], resetMsg);
else
// could be different bot
messageToPlayer(roConns[pn], new SOCRobotDismiss(gaName));
}
}
if (!reBoard.hadRobots)
resetBoardAndNotify_finish(reBoard, reGame);
// else
// gameState is READY_RESET_WAIT_ROBOT_DISMISS,
// and once the last robot leaves this game,
// SOCServerMessageHandler.handleLEAVEGAME will take care of the reset,
// by calling resetBoardAndNotify_finish.
}
Aggregations