use of soc.message.SOCGameState in project JSettlers2 by jdmonin.
the class SOCGameHandler method sendGameState.
/**
* Send all game members the current state of the game with a message.
* May also send other messages to the current player.
* Note that the current (or new) player number is not sent here.
* If game is now OVER, sends appropriate messages.
*<P>
* State {@link SOCGame#WAITING_FOR_DISCARDS}:
* If a 7 is rolled, will also say who must discard (in a GAMETEXTMSG).
*<P>
* State {@link SOCGame#WAITING_FOR_ROB_CHOOSE_PLAYER}:
* If current player must choose which player to rob,
* will also prompt their client to choose (in a CHOOSEPLAYERREQUEST).
*<P>
* State {@link SOCGame#STARTS_WAITING_FOR_PICK_GOLD_RESOURCE}:
* To announce the player must pick a resource to gain from the gold hex initial placement,
* please call {@link #sendGameState_sendGoldPickAnnounceText(SOCGame, String, Connection, SOCGame.RollResult)}.
*<P>
* State {@link SOCGame#WAITING_FOR_PICK_GOLD_RESOURCE}:
* If a gold hex is rolled, does not say who
* must pick resources to gain (because of timing). Please call
* {@link #sendGameState_sendGoldPickAnnounceText(SOCGame, String, Connection, SOCGame.RollResult)}
* after sending the resource gain text ("x gets 1 sheep").
*<P>
* <b>Note:</b> If game is now {@link SOCGame#OVER OVER} and the {@link SOCGame#isBotsOnly} flag is set,
* {@link #sendGameStateOVER(SOCGame)} will call {@link SOCServer#destroyGameAndBroadcast(String, String)}.
* Be sure that callers to {@code sendGameState} don't assume the game will still exist after calling this method.
* Also, {@code destroyGame} might create more {@link SOCGame#isBotsOnly} games, depending on server settings.
*<P>
* <b>Locks:</b> Does not hold {@link SOCGameList#takeMonitor()} or
* {@link SOCGameList#takeMonitorForGame}<tt>(gaName)</tt> when called.
* Some callers call {@link SOCGame#takeMonitor()} before calling; not important here.
*
* @see #sendTurn(SOCGame, boolean)
* @see #sendGameState(SOCGame)
* @see #sendGameStateOVER(SOCGame)
*
* @param ga the game
* @param omitGameStateMessage if true, don't send the {@link SOCGameState} message itself
* but do send any other messages as described above. For use just after sending a message which
* includes a Game State field.
* @param sendRollPrompt If true, and if we send a text message to prompt
* the player to roll, send a RollDicePrompt data message.
* If the client is too old (1.0.6), it will ignore the prompt.
*
* @return did we send a text message to prompt the player to roll?
* If so, sendTurn can also send a RollDicePrompt data message.
* @since 1.1.00
*/
boolean sendGameState(SOCGame ga, final boolean omitGameStateMessage, final boolean sendRollPrompt) {
if (ga == null)
return false;
final int gaState = ga.getGameState();
final int cpn = ga.getCurrentPlayerNumber();
final String gname = ga.getName();
boolean promptedRoll = false;
if (gaState == SOCGame.OVER) {
/**
* Before sending state "OVER", enforce current player number.
* This helps the client's copy of game recognize winning condition.
*/
srv.messageToGame(gname, (ga.clientVersionLowest >= SOCGameElements.MIN_VERSION) ? new SOCGameElements(gname, SOCGameElements.CURRENT_PLAYER, cpn) : new SOCSetTurn(gname, cpn));
}
if (!omitGameStateMessage)
srv.messageToGame(gname, new SOCGameState(gname, gaState));
SOCPlayer player = null;
if (cpn != -1)
player = ga.getPlayer(cpn);
switch(gaState) {
case SOCGame.START1A:
case SOCGame.START2A:
case SOCGame.START3A:
// "It's Joe's turn to build a settlement."
srv.messageToGameKeyed(ga, true, "prompt.turn.to.build.stlmt", player.getName());
if ((gaState >= SOCGame.START2A) && ga.isGameOptionSet(SOCGameOption.K_SC_3IP)) {
// reminder to player before their 2nd, 3rd settlements
Connection con = srv.getConnection(player.getName());
if (con != null) {
srv.messageToPlayerKeyed(con, gname, "prompt.gameopt._SC_3IP.part1");
// "This game gives you 3 initial settlements and roads."
srv.messageToPlayerKeyed(con, gname, "prompt.gameopt._SC_3IP.part2");
// "Your free resources will be from the third settlement."
}
}
break;
case SOCGame.START1B:
case SOCGame.START2B:
case SOCGame.START3B:
srv.messageToGameKeyed(ga, true, (// "It's Joe's turn to build a road or ship."
(ga.hasSeaBoard) ? // "It's Joe's turn to build a road or ship."
"prompt.turn.to.build.road.or.ship" : "prompt.turn.to.build.road"), player.getName());
break;
case SOCGame.ROLL_OR_CARD:
// "It's Joe's turn to roll the dice."
srv.messageToGameKeyed(ga, true, "prompt.turn.to.roll.dice", player.getName());
promptedRoll = true;
if (sendRollPrompt)
srv.messageToGame(gname, new SOCRollDicePrompt(gname, player.getPlayerNumber()));
break;
case SOCGame.WAITING_FOR_DISCARDS:
{
ArrayList<String> names = new ArrayList<String>();
for (int i = 0; i < ga.maxPlayers; i++) if (ga.getPlayer(i).getNeedToDiscard())
names.add(ga.getPlayer(i).getName());
if (names.size() == 1)
// "Joe needs to discard"
srv.messageToGameKeyed(ga, true, "prompt.discard.1", names.get(0));
else
// "Joe and Ed need to discard"
srv.messageToGameKeyedSpecial(ga, true, "prompt.discard.n", names);
}
break;
case SOCGame.WAITING_FOR_ROBBER_OR_PIRATE:
// "{0} must choose to move the robber or the pirate."
srv.messageToGameKeyed(ga, true, "robber.willmove.choose", player.getName());
break;
case SOCGame.PLACING_ROBBER:
// "{0} will move the robber."
srv.messageToGameKeyed(ga, true, "robber.willmove", player.getName());
break;
case SOCGame.PLACING_PIRATE:
// "{0} will move the pirate ship."
srv.messageToGameKeyed(ga, true, "robber.willmove.pirate", player.getName());
break;
case SOCGame.WAITING_FOR_ROB_CHOOSE_PLAYER:
/**
* get the choices from the game
*/
final boolean canStealNone = ga.isGameOptionSet(SOCGameOption.K_SC_PIRI);
boolean[] choices = new boolean[ga.maxPlayers + (canStealNone ? 1 : 0)];
Arrays.fill(choices, false);
if (canStealNone)
choices[ga.maxPlayers] = true;
for (SOCPlayer pl : ga.getPossibleVictims()) choices[pl.getPlayerNumber()] = true;
/**
* ask the current player to choose a player to steal from
*/
Connection con = srv.getConnection(ga.getPlayer(cpn).getName());
if (con != null) {
con.put(SOCChoosePlayerRequest.toCmd(gname, choices));
}
break;
case SOCGame.OVER:
sendGameStateOVER(ga);
break;
}
return promptedRoll;
}
use of soc.message.SOCGameState in project JSettlers2 by jdmonin.
the class SOCGameHandler method startGame.
// javadoc inherited from GameHandler
/**
* {@inheritDoc}
*<P>
* If {@link SOCGame#hasSeaBoard}: Once the board is made, send the updated
* {@link SOCPotentialSettlements potential settlements}.
*<P>
* If this code changes, must also update {@link soctest.TestBoardLayouts#testSingleLayout(SOCScenario, int)}.
*/
public void startGame(SOCGame ga) {
if (ga == null)
return;
final String gaName = ga.getName();
// TODO once multiple handler threads, encapsulate this
srv.numberOfGamesStarted++;
/**
* start the game, place any initial pieces.
* If anything is added to this game object setup code,
* update soctest.TestBoardLayouts.testSingleLayout(..).
*/
// for playerEvent, gameEvent callbacks (since 2.0.00)
ga.setScenarioEventListener(this);
ga.startGame();
// used on sea board; if null, all are legal
final int[][] legalSeaEdges;
if (ga.hasSeaBoard) {
legalSeaEdges = SOCBoardAtServer.getLegalSeaEdges(ga, -1);
if (legalSeaEdges != null)
for (int pn = 0; pn < ga.maxPlayers; ++pn) ga.getPlayer(pn).setRestrictedLegalShips(legalSeaEdges[pn]);
if (ga.isGameOptionSet(SOCGameOption.K_SC_FTRI) || ga.isGameOptionSet(SOCGameOption.K_SC_PIRI)) {
// scenario has initial pieces
((SOCBoardAtServer) (ga.getBoard())).startGame_putInitPieces(ga);
}
} else {
legalSeaEdges = null;
}
srv.gameList.takeMonitorForGame(gaName);
try {
/**
* send the board layout
*/
try {
srv.messageToGameWithMon(gaName, getBoardLayoutMessage(ga));
// For scenario option _SC_CLVI, the board layout message
// includes villages and the general supply cloth count.
// For _SC_PIRI, it includes the Pirate Path (additional layout part "PP").
} catch (IllegalArgumentException e) {
System.err.println("startGame: Cannot send board for " + gaName + ": " + e.getMessage());
// the enclosing try-finally will releaseMonitorForGame(gaName) before returning
return;
}
if (ga.hasSeaBoard) {
// See also joinGame which has very similar code.
// Send the updated Potential/Legal Settlement node list
// Note: Assumes all players have same potential settlements
// (sends with playerNumber -1 == all)
final HashSet<Integer> psList = ga.getPlayer(0).getPotentialSettlements();
// Some boards may have multiple land areas.
final HashSet<Integer>[] lan;
final int pan;
boolean addedPsList = false;
final SOCBoardLarge bl = (SOCBoardLarge) ga.getBoard();
lan = bl.getLandAreasLegalNodes();
pan = bl.getStartingLandArea();
if ((lan != null) && (pan != 0) && !lan[pan].equals(psList)) {
// If potentials != legals[startingLandArea], send as legals[0]
lan[0] = psList;
addedPsList = true;
}
if (lan == null)
srv.messageToGameWithMon(gaName, new SOCPotentialSettlements(gaName, -1, new ArrayList<Integer>(psList)));
else
srv.messageToGameWithMon(gaName, new SOCPotentialSettlements(gaName, -1, pan, lan, legalSeaEdges));
if (addedPsList)
// Undo change to game's copy of landAreasLegalNodes
lan[0] = null;
}
/**
* send the player info
*/
boolean sentInitPiecesState = false;
for (int i = 0; i < ga.maxPlayers; i++) {
if (ga.isSeatVacant(i))
continue;
final SOCPlayer pl = ga.getPlayer(i);
final int[] counts = new int[(ga.hasSeaBoard) ? 4 : 3];
counts[0] = pl.getNumPieces(SOCPlayingPiece.ROAD);
counts[1] = pl.getNumPieces(SOCPlayingPiece.SETTLEMENT);
counts[2] = pl.getNumPieces(SOCPlayingPiece.CITY);
if (ga.hasSeaBoard) {
// Some scenarios like SC_PIRI may place initial pieces at fixed locations.
// Usually, pieces will be empty.
final Vector<SOCPlayingPiece> pieces = pl.getPieces();
if (!pieces.isEmpty()) {
if (!sentInitPiecesState) {
// Temporary state change, to avoid initial-piece placement actions.
// The actual game state will be sent soon.
srv.messageToGameWithMon(gaName, new SOCGameState(gaName, SOCGame.READY));
sentInitPiecesState = true;
}
for (SOCPlayingPiece pp : pieces) srv.messageToGameWithMon(gaName, new SOCPutPiece(gaName, i, pp.getType(), pp.getCoordinates()));
SOCPlayingPiece pp = pl.getFortress();
if (pp != null)
srv.messageToGameWithMon(gaName, new SOCPutPiece(gaName, i, pp.getType(), pp.getCoordinates()));
}
counts[3] = pl.getNumPieces(SOCPlayingPiece.SHIP);
}
if (ga.clientVersionLowest >= SOCPlayerElements.MIN_VERSION)
srv.messageToGameWithMon(gaName, new SOCPlayerElements(gaName, i, SOCPlayerElement.SET, (ga.hasSeaBoard) ? ELEM_PIECETYPES_SEA : ELEM_PIECETYPES_CLASSIC, counts));
else
for (int j = 0; j < counts.length; ++j) srv.messageToGameWithMon(gaName, new SOCPlayerElement(gaName, i, SOCPlayerElement.SET, ELEM_PIECETYPES_SEA[j], counts[j]));
if (ga.clientVersionLowest < SOCPlayerElement.VERSION_FOR_CARD_ELEMENTS)
srv.messageToGameWithMon(gaName, new SOCSetPlayedDevCard(gaName, i, false));
}
if (ga.clientVersionLowest >= SOCPlayerElement.VERSION_FOR_CARD_ELEMENTS)
srv.messageToGameWithMon(gaName, new SOCPlayerElement(gaName, -1, SOCPlayerElement.SET, SOCPlayerElement.PLAYED_DEV_CARD_FLAG, 0));
/**
* send the number of dev cards
*/
srv.messageToGameWithMon(gaName, (ga.clientVersionLowest >= SOCGameElements.MIN_VERSION) ? new SOCGameElements(gaName, SOCGameElements.DEV_CARD_COUNT, ga.getNumDevCards()) : new SOCDevCardCount(gaName, ga.getNumDevCards()));
/**
* ga.startGame() picks who goes first, but feedback is nice
*/
srv.messageToGameKeyed(ga, false, // "Randomly picking a starting player..."
"start.picking.random.starting.player");
} finally {
srv.gameList.releaseMonitorForGame(gaName);
}
/**
* send the game state and start the game.
* send game state and whose turn it is.
*/
if (ga.clientVersionLowest >= SOCGameState.VERSION_FOR_GAME_STATE_AS_FIELD) {
srv.messageToGame(gaName, new SOCStartGame(gaName, ga.getGameState()));
sendTurn(ga, false);
} else {
final int cpn = ga.getCurrentPlayerNumber();
final boolean sendRoll = sendGameState(ga, false, false);
srv.messageToGame(gaName, new SOCStartGame(gaName, 0));
srv.messageToGame(gaName, new SOCTurn(gaName, cpn, 0));
if (sendRoll)
srv.messageToGame(gaName, new SOCRollDicePrompt(gaName, cpn));
}
}
Aggregations