use of soc.message.SOCCancelBuildRequest in project JSettlers2 by jdmonin.
the class SOCGameHandler method endGameTurnOrForce.
/**
* A bot is unresponsive, or a human player has left the game.
* End this player's turn cleanly, or force-end if needed.
*<P>
* Can be called for a player still in the game, or for a player
* who has left ({@link SOCGame#removePlayer(String)} has been called).
* Can be called for a player who isn't current player; in that case
* it takes action if the game was waiting for the player (picking random
* resources for discard or gold-hex picks) but won't end the current turn.
*<P>
* If they were placing an initial road, also cancels that road's
* initial settlement.
*<P>
* <b>Locks:</b> Must not have ga.takeMonitor() when calling this method.
* May or may not have <tt>gameList.takeMonitorForGame(ga)</tt>;
* use <tt>hasMonitorFromGameList</tt> to indicate.
*<P>
* Not public, but package visibility, for use by {@link SOCForceEndTurnThread} for {@link SOCGameTimeoutChecker}.
*
* @param ga The game to end turn if called for current player, or to otherwise stop waiting for a player
* @param plNumber player.getNumber; may or may not be current player
* @param plName player.getName
* @param plConn player's client connection
* @param hasMonitorFromGameList if false, have not yet called
* {@link SOCGameList#takeMonitorForGame(String) gameList.takeMonitorForGame(ga)};
* if false, this method will take this monitor at its start,
* and release it before returning.
* @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 OVER}.
*/
boolean endGameTurnOrForce(SOCGame ga, final int plNumber, final String plName, Connection plConn, final boolean hasMonitorFromGameList) {
boolean gameStillActive = true;
final String gaName = ga.getName();
if (!hasMonitorFromGameList) {
srv.gameList.takeMonitorForGame(gaName);
}
final int cpn = ga.getCurrentPlayerNumber();
final int gameState = ga.getGameState();
/**
* Is a board-reset vote is in progress?
* If they're still a sitting player, to keep the game
* moving, fabricate their response: vote No.
*/
boolean gameVotingActiveDuringStart = false;
if (ga.getResetVoteActive()) {
if (gameState <= SOCGame.STARTS_WAITING_FOR_PICK_GOLD_RESOURCE)
gameVotingActiveDuringStart = true;
if ((!ga.isSeatVacant(plNumber)) && (ga.getResetPlayerVote(plNumber) == SOCGame.VOTE_NONE)) {
srv.gameList.releaseMonitorForGame(gaName);
ga.takeMonitor();
srv.resetBoardVoteNotifyOne(ga, plNumber, plName, false);
ga.releaseMonitor();
srv.gameList.takeMonitorForGame(gaName);
}
}
/**
* Now end their turn, or handle any needed responses if not current player.
* Don't call forceEndGameTurn()/ga.forceEndTurn() unless we need to.
*/
if (plNumber == cpn) {
if ((gameState == SOCGame.START1B) || (gameState == SOCGame.START2B) || (gameState == SOCGame.START3B)) {
/**
* Leaving during initial road placement.
* Cancel the settlement they just placed,
* and send that cancel to the other players.
* Don't change gameState yet.
* Note that their most recent init settlement is removed here,
* but not earlier settlement(s). (That would impact the robots much more.)
*/
SOCPlayer pl = ga.getPlayer(plNumber);
SOCSettlement pp = new SOCSettlement(pl, pl.getLastSettlementCoord(), null);
ga.undoPutInitSettlement(pp);
// state was changed by undoPutInitSettlement
ga.setGameState(gameState);
srv.messageToGameWithMon(gaName, new SOCCancelBuildRequest(gaName, SOCSettlement.SETTLEMENT));
}
if (ga.canEndTurn(plNumber)) {
srv.gameList.releaseMonitorForGame(gaName);
ga.takeMonitor();
endGameTurn(ga, null, true);
ga.releaseMonitor();
srv.gameList.takeMonitorForGame(gaName);
} else {
/**
* Cannot easily end turn.
* Must back out something in progress.
* May or may not end turn; see javadocs
* of forceEndGameTurn and game.forceEndTurn.
* All start phases are covered here (START1A..START2B)
* because canEndTurn returns false in those gameStates.
*/
srv.gameList.releaseMonitorForGame(gaName);
ga.takeMonitor();
if (gameVotingActiveDuringStart) {
/**
* If anyone has requested a board-reset vote during
* game-start phases, we have to tell clients to cancel
* the vote request, because {@link soc.message.SOCTurn}
* isn't always sent during start phases. (Voting must
* end when the turn ends.)
*/
srv.messageToGame(gaName, new SOCResetBoardReject(gaName));
ga.resetVoteClear();
}
/**
* Force turn to end
*/
gameStillActive = forceEndGameTurn(ga, plName);
ga.releaseMonitor();
if (gameStillActive) {
srv.gameList.takeMonitorForGame(gaName);
}
}
} else {
/**
* Check if game is waiting for input from the player who
* is leaving, but who isn't current player.
* To keep the game moving, fabricate their response.
* - Board-reset voting: Handled above.
* - Waiting for discard: Handle here.
* - Waiting for gold-hex pick: Handle here.
*/
if (((gameState == SOCGame.WAITING_FOR_DISCARDS) && ga.getPlayer(plNumber).getNeedToDiscard()) || (((gameState == SOCGame.WAITING_FOR_PICK_GOLD_RESOURCE) || (gameState == SOCGame.STARTS_WAITING_FOR_PICK_GOLD_RESOURCE)) && (ga.getPlayer(plNumber).getNeedToPickGoldHexResources() > 0))) {
/**
* For discard, tell the discarding player's client that they discarded the resources,
* tell everyone else that the player discarded unknown resources.
* For gold pick, announce the picked resources.
*/
srv.gameList.releaseMonitorForGame(gaName);
System.err.println("L5789: Waiting too long for bot discard or gain: game=" + ga.getName() + ", pn=" + plNumber + " " + plName);
ga.takeMonitor();
forceGamePlayerDiscardOrGain(ga, cpn, plConn, plName, plNumber);
// WAITING_FOR_DISCARDS or MOVING_ROBBER for discard;
sendGameState(ga, false, false);
// PLAY1 or WAITING_FOR_PICK_GOLD_RESOURCE for gain
ga.releaseMonitor();
srv.gameList.takeMonitorForGame(gaName);
}
}
if (!hasMonitorFromGameList) {
srv.gameList.releaseMonitorForGame(gaName);
}
return gameStillActive;
}
Aggregations