use of soc.server.genericServer.Connection in project JSettlers2 by jdmonin.
the class SOCGameHandler method findRobotAskJoinGame.
/**
* When a human player has left an active game, or a game is starting and a
* bot from that game's {@link SOCServer#robotJoinRequests} has disconnected,
* look for a robot player which can take a seat and continue the game.
*<P>
* If found the bot will be added to {@link SOCServer#robotJoinRequests} and
* sent a {@link SOCBotJoinGameRequest}. Otherwise the game will be sent a
* {@link SOCGameServerText} explaining failure to find any robot; human players
* might need to leave the game and start a new one.
* @param ga Game to look in
* @param seatNumberObj Seat number to fill, as an {@link Integer} object.
* If {@code ! gameIsActive}, this comes from {@link SOCServer#robotJoinRequests}
* via {@link SOCServer#leaveConnection(Connection)}.
* @param gameIsActive True if for active game, not a game still starting
* @return true if an available bot was found
* @since 2.0.00
*/
public boolean findRobotAskJoinGame(final SOCGame ga, Object seatNumberObj, final boolean gameIsActive) {
if (gameIsActive)
// "Fetching a robot player..."
srv.messageToGameKeyed(ga, false, "member.bot.join.fetching");
if (srv.robots.isEmpty()) {
srv.messageToGameKeyed(ga, false, "member.bot.join.no.bots.server");
// <--- Early return: No bot available ---
return false;
} else if (ga.getClientVersionMinRequired() > Version.versionNumber()) {
srv.messageToGameKeyed(ga, false, "member.bot.join.interror.version", ga.getClientVersionMinRequired());
// <--- Early return: No bot available ---
return false;
}
// the bot selected to join
Connection robotConn = null;
// false if can select a bot that isn't already playing in or requested in this game
boolean nameMatch = true;
final String gaName = ga.getName();
Hashtable<Connection, Object> requestedBots = srv.robotJoinRequests.get(gaName);
if (// should not happen; check just in case
!(seatNumberObj instanceof Integer)) {
seatNumberObj = null;
// nameMatch remains true; will announce can't find a bot
} else {
/**
* request a robot that isn't already playing this game or
* is not already requested to play in this game
*/
final HashSet<String> gameBots = new HashSet<String>();
for (int i = 0; i < ga.maxPlayers; i++) {
SOCPlayer pl = ga.getPlayer(i);
if (pl != null) {
String pname = pl.getName();
if (pname != null)
gameBots.add(pname);
}
}
// Shuffle to distribute load
final int[] robotIndexes = srv.robotShuffleForJoin();
for (int idx = 0; idx < srv.robots.size(); idx++) {
robotConn = srv.robots.get(robotIndexes[idx]);
nameMatch = gameBots.contains(robotConn.getData());
if ((!nameMatch) && (requestedBots != null))
nameMatch = requestedBots.containsKey(robotConn);
if (!nameMatch)
break;
}
}
if (!nameMatch) {
/**
* make the request
*/
D.ebugPrintln("@@@ JOIN GAME REQUEST for " + robotConn.getData());
final int seatNumber = ((Integer) seatNumberObj).intValue();
if (ga.getSeatLock(seatNumber) != SOCGame.SeatLockState.UNLOCKED) {
// make sure bot can sit
ga.setSeatLock(seatNumber, SOCGame.SeatLockState.UNLOCKED);
srv.messageToGameWithMon(gaName, new SOCSetSeatLock(gaName, seatNumber, SOCGame.SeatLockState.UNLOCKED));
}
/**
* record the request
*/
if (requestedBots == null) {
requestedBots = new Hashtable<Connection, Object>();
requestedBots.put(robotConn, seatNumberObj);
srv.robotJoinRequests.put(gaName, requestedBots);
} else {
requestedBots.put(robotConn, seatNumberObj);
}
robotConn.put(SOCBotJoinGameRequest.toCmd(gaName, seatNumber, ga.getGameOptions()));
} else {
// "*** Can't find a robot! ***"
srv.messageToGameKeyed(ga, false, "member.bot.join.cantfind");
// <--- Early return: No bot available ---
return false;
}
return true;
}
use of soc.server.genericServer.Connection in project JSettlers2 by jdmonin.
the class SOCGameHandler method sendGamePendingMessages.
/**
* Sends the contents of this game's {@link SOCGame#pendingMessagesOut}, then empties that list.
* To avoid unnecessary work here, check if the list is empty before calling this method.
*<P>
* <B>I18N:</B> Checks {@code pendingMessagesOut} for {@link SOCKeyedMessage}s and handles them accordingly.
* Currently this is the only method that checks for those, because other places send text messages
* immediately instead of queueing them and localizing/sending later.
* Also checks for {@link UnlocalizedString}s, to be localized and sent with
* {@link SOCServer#messageToGameKeyed(SOCGame, boolean, String, Object...)}
* or {@link SOCServer#messageToGameKeyedSpecial(SOCGame, boolean, String, Object...)}.
*<P>
* <B>Locks:</B> If {@code takeMon} is true, takes and releases
* {@link SOCGameList#takeMonitorForGame(String) gameList.takeMonitorForGame(gameName)}.
* Otherwise call {@link SOCGameList#takeMonitorForGame(String) gameList.takeMonitorForGame(gameName)}
* before calling this method.
* @param ga game with pending messages
* @param takeMon Should this method take and release game's monitor via
* {@link SOCGameList#takeMonitorForGame(String) gameList.takeMonitorForGame(gameName)}?
* True unless caller already holds that monitor.
* @see #updatePlayerSVPPendingMessage(SOCGame, SOCPlayer, int, String)
* @since 2.0.00
*/
void sendGamePendingMessages(SOCGame ga, final boolean takeMon) {
final String gaName = ga.getName();
if (takeMon)
srv.gameList.takeMonitorForGame(gaName);
for (final Object msg : ga.pendingMessagesOut) {
if (msg instanceof SOCKeyedMessage)
srv.messageToGameKeyedType(ga, (SOCKeyedMessage) msg, false);
else if (msg instanceof SOCMessage)
srv.messageToGameWithMon(gaName, (SOCMessage) msg);
else if (msg instanceof UnlocalizedString) {
final UnlocalizedString us = (UnlocalizedString) msg;
if (us.isSpecial)
srv.messageToGameKeyedSpecial(ga, false, us.key, us.params);
else
srv.messageToGameKeyed(ga, false, us.key, us.params);
}
// else: ignore
}
ga.pendingMessagesOut.clear();
for (SOCPlayer p : ga.getPlayers()) {
final List<Object> pq = p.pendingMessagesOut;
final int L = pq.size();
if (L >= 0) {
final Connection c = srv.getConnection(p.getName());
if (c != null)
for (int i = 0; i < L; ++i) c.put(((SOCMessage) pq.get(i)).toCmd());
pq.clear();
}
}
if (takeMon)
srv.gameList.releaseMonitorForGame(gaName);
}
use of soc.server.genericServer.Connection in project JSettlers2 by jdmonin.
the class SOCGameHandler method reportRobbery.
/**
* The current player is stealing from another player.
* Send messages saying what was stolen.
*
* @param ga the game
* @param pe the perpetrator
* @param vi the victim
* @param rsrc type of resource stolen, as in {@link SOCResourceConstants#SHEEP},
* or {@link SOCResourceConstants#CLOTH_STOLEN_LOCAL} for cloth
* (scenario option {@link SOCGameOption#K_SC_CLVI _SC_CLVI}).
*/
void reportRobbery(SOCGame ga, SOCPlayer pe, SOCPlayer vi, final int rsrc) {
if (ga == null)
return;
final String gaName = ga.getName();
final String peName = pe.getName();
final String viName = vi.getName();
final int pePN = pe.getPlayerNumber();
final int viPN = vi.getPlayerNumber();
if (rsrc == SOCResourceConstants.CLOTH_STOLEN_LOCAL) {
// Send players' cloth counts and text.
// Client's game will recalculate players' VP based on
// the cloth counts, so we don't need to also send VP.
srv.messageToGame(gaName, new SOCPlayerElement(gaName, viPN, SOCPlayerElement.SET, SOCPlayerElement.SCENARIO_CLOTH_COUNT, vi.getCloth(), true));
srv.messageToGame(gaName, new SOCPlayerElement(gaName, pePN, SOCPlayerElement.SET, SOCPlayerElement.SCENARIO_CLOTH_COUNT, pe.getCloth()));
// "{0} stole a cloth from {1}."
srv.messageToGameKeyed(ga, true, "robber.stole.cloth.from", peName, viName);
// <--- early return: cloth is announced to entire game ---
return;
}
SOCPlayerElement gainRsrc = null;
SOCPlayerElement loseRsrc = null;
SOCPlayerElement gainUnknown;
SOCPlayerElement loseUnknown;
// This works because SOCPlayerElement.SHEEP == SOCResourceConstants.SHEEP.
gainRsrc = new SOCPlayerElement(gaName, pePN, SOCPlayerElement.GAIN, rsrc, 1);
loseRsrc = new SOCPlayerElement(gaName, viPN, SOCPlayerElement.LOSE, rsrc, 1, true);
/**
* send the game data messages
*/
Connection peCon = srv.getConnection(peName);
Connection viCon = srv.getConnection(viName);
srv.messageToPlayer(peCon, gainRsrc);
srv.messageToPlayer(peCon, loseRsrc);
srv.messageToPlayer(viCon, gainRsrc);
srv.messageToPlayer(viCon, loseRsrc);
// Don't send generic message to pe or vi
List<Connection> sendNotTo = new ArrayList<Connection>(2);
sendNotTo.add(peCon);
sendNotTo.add(viCon);
gainUnknown = new SOCPlayerElement(gaName, pePN, SOCPlayerElement.GAIN, SOCPlayerElement.UNKNOWN, 1);
loseUnknown = new SOCPlayerElement(gaName, viPN, SOCPlayerElement.LOSE, SOCPlayerElement.UNKNOWN, 1);
srv.messageToGameExcept(gaName, sendNotTo, gainUnknown, true);
srv.messageToGameExcept(gaName, sendNotTo, loseUnknown, true);
/**
* send the text messages:
* "You stole a sheep from viName." [In v1.x.xx, "stole a sheep resource"]
* "peName stole a sheep from you."
* "peName stole a resource from viName."
*/
// "You stole {0,rsrcs} from {2}."
srv.messageToPlayerKeyedSpecial(peCon, ga, "robber.you.stole.resource.from", -1, rsrc, viName);
// "{0} stole {1,rsrcs} from you."
srv.messageToPlayerKeyedSpecial(viCon, ga, "robber.stole.resource.from.you", peName, -1, rsrc);
// "{0} stole a resource from {1}."
srv.messageToGameKeyedSpecialExcept(ga, true, sendNotTo, "robber.stole.resource.from", peName, viName);
}
use of soc.server.genericServer.Connection 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.server.genericServer.Connection in project JSettlers2 by jdmonin.
the class SOCGameHandler method playerEvent.
/**
* Listener callback for per-player scenario events on the large sea board.
* For example, there might be an SVP awarded for settlements.
* Server sends messages to the game to announce it (PLAYERELEMENT,
* {@link #updatePlayerSVPPendingMessage(SOCGame, SOCPlayer, int, String)}, etc).
*<P>
* <em>Threads:</em> The game's treater thread handles incoming client messages and calls
* game methods that change state. Those same game methods will trigger the scenario events;
* so, the treater thread will also run this <tt>playerEvent</tt> callback.
*
* @param ga Game
* @param pl Player
* @param evt Event code
* @see #gameEvent(SOCGame, SOCScenarioGameEvent, Object)
* @param flagsChanged True if this event changed {@link SOCPlayer#getScenarioPlayerEvents()},
* {@link SOCPlayer#getSpecialVP()}, or another flag documented for <tt>evt</tt> in
* {@link SOCScenarioPlayerEvent}
* @param obj Object related to the event, or null; documented for <tt>evt</tt> in {@link SOCScenarioPlayerEvent}.
* Example: The {@link SOCVillage} for {@link SOCScenarioPlayerEvent#CLOTH_TRADE_ESTABLISHED_VILLAGE}.
* @since 2.0.00
*/
public void playerEvent(final SOCGame ga, final SOCPlayer pl, final SOCScenarioPlayerEvent evt, final boolean flagsChanged, final Object obj) {
// Note: Some SOCGameHandler code assumes that player events are fired only during
// SOCGameMessageHandler.handlePUTPIECE and handleMOVEPIECE.
// Most handle* methods don't check pendingMessagesOut before sending game state.
// If a new player event breaks this assumption, adjust SOCGameHandler.playerEvent(...)
// and related code; search where SOCGame.pendingMessagesOut is used.
final String gaName = ga.getName(), plName = pl.getName();
final int pn = pl.getPlayerNumber();
boolean sendSVP = true;
boolean sendPlayerEventsBitmask = true;
switch(evt) {
case SVP_SETTLED_ANY_NEW_LANDAREA:
{
final String newSettleEventStr = (playerEvent_newSettlementIsByShip(ga, (SOCSettlement) obj)) ? // "growing past the main island"
"event.svp.sc_sany.island" : // "growing to a new area"
"event.svp.sc_sany.area";
updatePlayerSVPPendingMessage(ga, pl, 1, newSettleEventStr);
}
break;
case SVP_SETTLED_EACH_NEW_LANDAREA:
{
final String newSettleEventStr = (playerEvent_newSettlementIsByShip(ga, (SOCSettlement) obj)) ? // "settling a new island"
"event.svp.sc_seac.island" : // "settling a new area"
"event.svp.sc_seac.area";
updatePlayerSVPPendingMessage(ga, pl, 2, newSettleEventStr);
sendPlayerEventsBitmask = false;
final int las = pl.getScenarioSVPLandAreas();
if (las != 0)
ga.pendingMessagesOut.add(new SOCPlayerElement(gaName, pn, SOCPlayerElement.SET, SOCPlayerElement.SCENARIO_SVP_LANDAREAS_BITMASK, las));
}
break;
case CLOTH_TRADE_ESTABLISHED_VILLAGE:
{
sendSVP = false;
if (!flagsChanged)
sendPlayerEventsBitmask = false;
ga.pendingMessagesOut.add(new UnlocalizedString("event.sc_clvi.established", // "{0} established a trade route with a village."
plName));
if (flagsChanged)
srv.messageToPlayerPendingKeyed(pl, gaName, "event.sc_clvi.not.prevented.pirate");
// "You are no longer prevented from moving the pirate ship."
// Player gets 1 cloth for establishing trade
SOCVillage vi = (SOCVillage) obj;
srv.messageToGame(gaName, new SOCPieceValue(gaName, vi.getCoordinates(), vi.getCloth(), 0));
srv.messageToGame(gaName, new SOCPlayerElement(gaName, pn, SOCPlayerElement.SET, SOCPlayerElement.SCENARIO_CLOTH_COUNT, pl.getCloth()));
}
break;
case DEV_CARD_REACHED_SPECIAL_EDGE:
{
sendPlayerEventsBitmask = false;
sendSVP = false;
IntPair edge_cardType = (IntPair) obj;
Connection c = srv.getConnection(plName);
ga.pendingMessagesOut.add(new UnlocalizedString("action.built.sc_ftri.dev", // "{0} gets a Development Card as a gift from the Lost Tribe."
plName));
srv.messageToPlayer(c, new SOCDevCardAction(gaName, pn, SOCDevCardAction.DRAW, edge_cardType.getB()));
srv.messageToGameExcept(gaName, c, new SOCDevCardAction(gaName, pn, SOCDevCardAction.DRAW, SOCDevCardConstants.UNKNOWN), true);
srv.messageToGame(gaName, new SOCSimpleAction(gaName, -1, SOCSimpleAction.BOARD_EDGE_SET_SPECIAL, edge_cardType.getA(), 0));
}
break;
case SVP_REACHED_SPECIAL_EDGE:
{
// "a gift from the Lost Tribe"
updatePlayerSVPPendingMessage(ga, pl, 1, "event.svp.sc_ftri.gift");
sendPlayerEventsBitmask = false;
srv.messageToGame(gaName, new SOCSimpleAction(gaName, -1, SOCSimpleAction.BOARD_EDGE_SET_SPECIAL, ((Integer) obj).intValue(), 0));
}
break;
case REMOVED_TRADE_PORT:
{
sendPlayerEventsBitmask = false;
sendSVP = false;
IntPair edge_portType = (IntPair) obj;
final int edge = edge_portType.getA(), portType = edge_portType.getB();
if ((edge & 0xFF) <= ga.getBoard().getBoardWidth())
// announce removal from board, unless (for debugging)
// this port wasn't really on the board at clients
srv.messageToGame(gaName, new SOCSimpleAction(gaName, pn, SOCSimpleAction.TRADE_PORT_REMOVED, edge, portType));
if (ga.getGameState() == SOCGame.PLACING_INV_ITEM) {
// Removal happens during ship piece placement, which is followed at server with sendGameState.
// When sendGameState gives the new state, client will prompt current player to place now.
// We just need to send the client PLACING_EXTRA, for the port type and not-cancelable flag.
Connection c = srv.getConnection(plName);
srv.messageToPlayer(c, new SOCInventoryItemAction(gaName, pn, SOCInventoryItemAction.PLACING_EXTRA, -portType, false, false, false));
} else {
// port was added to player's inventory;
// if this message changes, also update SOCGameHandler.processDebugCommand_scenario
srv.messageToGame(gaName, new SOCInventoryItemAction(gaName, pn, SOCInventoryItemAction.ADD_PLAYABLE, -portType, false, false, true));
}
}
break;
default:
// Suppress warning; not all enum values need a handler here
break;
}
if (sendSVP)
ga.pendingMessagesOut.add(new SOCPlayerElement(gaName, pn, SOCPlayerElement.SET, SOCPlayerElement.SCENARIO_SVP, pl.getSpecialVP()));
if (sendPlayerEventsBitmask)
ga.pendingMessagesOut.add(new SOCPlayerElement(gaName, pn, SOCPlayerElement.SET, SOCPlayerElement.SCENARIO_PLAYEREVENTS_BITMASK, pl.getScenarioPlayerEvents()));
}
Aggregations