use of soc.server.genericServer.Connection in project JSettlers2 by jdmonin.
the class SOCServer method setClientVersSendGamesOrReject.
/**
* Set client's version and locale, and check against minimum required version {@link #CLI_VERSION_MIN}.
* If version is too low, send {@link SOCRejectConnection REJECTCONNECTION}
* and call {@link #removeConnection(Connection, boolean)}.
* If we haven't yet sent the game list, send now.
* If we've already sent the game list, send changes based on true version.
*<P>
* Along with the game list, the client will need to know the game option info.
* This is sent when the client asks (after VERSION) for {@link SOCGameOptionGetInfos GAMEOPTIONGETINFOS}.
* Game options are sent after client version is known, so the list of
* sent options is based on client version.
*<P>
*<B>I18N:</B> If client doesn't send a locale string, the default locale {@code en_US} is used.
* Robot clients will get the default locale and localeStr here; those will be cleared to {@code null} by
* {@link #authOrRejectClientRobot(Connection, String, String, String)} when the bot sends {@link SOCImARobot}.
*<P>
*<b>Locks:</b> To set the version, will synchronize briefly on {@link Server#unnamedConns unnamedConns}.
* If {@link Connection#getVersion() c.getVersion()} is already == cvers,
* don't bother to lock and set it.
*<P>
* Package access (not private) is strictly for use of {@link SOCServerMessageHandler}
* and {@link SOCClientData.SOCCDCliVersionTask#run()}.
*
* @param c Client's connection
* @param cvers Version reported by client, or assumed version if no report
* @param clocale Locale reported by client, or null if none given (was added to {@link SOCVersion} in 2.0.00)
* @param isKnown Is this the client's definite version, or just an assumed one?
* Affects {@link Connection#isVersionKnown() c.isVersionKnown}.
* Can set the client's known version only once; a second "known" call with
* a different cvers will be rejected.
* @return True if OK, false if rejected
*/
boolean setClientVersSendGamesOrReject(Connection c, final int cvers, String clocale, final boolean isKnown) {
final int prevVers = c.getVersion();
final boolean wasKnown = c.isVersionKnown();
SOCClientData scd = (SOCClientData) c.getAppData();
// Message to send/log if client must be disconnected
String rejectMsg = null;
String rejectLogMsg = null;
if (clocale == null)
// backwards compatibility with clients older than v2.0.00
clocale = "en_US";
final int hashIdx = clocale.indexOf("_#");
if (hashIdx != -1) {
// extended info from java 1.7+ Locale.toString();
// if our server is an older JRE version, strip that out.
final String jreVersStr = System.getProperty("java.specification.version");
if (jreVersStr.startsWith("1.5") || jreVersStr.startsWith("1.6")) {
clocale = clocale.substring(0, hashIdx);
}
}
scd.localeStr = clocale;
try {
scd.locale = I18n.parseLocale(clocale);
} catch (IllegalArgumentException e) {
rejectMsg = "Sorry, cannot parse your locale.";
// unsanitized data, don't print clocale to log
rejectLogMsg = "Rejected client: Cannot parse locale";
}
c.setI18NStringManager(SOCStringManager.getServerManagerForClient(scd.locale), clocale);
if (prevVers == -1)
scd.clearVersionTimer();
if (prevVers != cvers) {
synchronized (unnamedConns) {
c.setVersion(cvers, isKnown);
}
} else if (wasKnown) {
// <--- Early return: Already knew it ----
return true;
}
if (cvers < CLI_VERSION_MIN) {
if (cvers > 0)
rejectMsg = "Sorry, your client version number " + cvers + " is too old, version ";
else
rejectMsg = "Sorry, your client version is too old, version number ";
rejectMsg += Integer.toString(CLI_VERSION_MIN) + " (" + CLI_VERSION_MIN_DISPLAY + ") or above is required.";
rejectLogMsg = "Rejected client: Version " + cvers + " too old";
}
if (wasKnown && isKnown && (cvers != prevVers)) {
// can't change the version once known
rejectMsg = "Sorry, cannot report two different versions.";
rejectLogMsg = "Rejected client: Already gave VERSION(" + prevVers + "), now says VERSION(" + cvers + ")";
}
if (rejectMsg != null) {
c.put(new SOCRejectConnection(rejectMsg).toCmd());
c.disconnectSoft();
System.out.println(rejectLogMsg);
// make an effort to send reject message before closing socket
final Connection rc = c;
miscTaskTimer.schedule(new TimerTask() {
public void run() {
removeConnection(rc, true);
}
}, 300);
// <--- Early return: Rejected client ---
return false;
}
// Send game list?
// Will check c.getAppData().hasSentGameList() flag.
// prevVers is ignored unless already sent game list.
sendGameList(c, prevVers);
// This will be displayed in the client's status line (v1.1.17 and newer).
if (allowDebugUser)
c.put(SOCStatusMessage.toCmd(SOCStatusMessage.SV_OK_DEBUG_MODE_ON, cvers, // "Debugging is On. Welcome to Java Settlers of Catan!"
c.getLocalized("member.welcome.debug")));
// Increment version stats; currently assumes single-threaded access to the map.
// We don't know yet if client is a bot, so bots are included in the stats.
// (If this is not wanted, the bot could be subtracted at handleIMAROBOT.)
final Integer cversObj = Integer.valueOf(cvers);
final int prevCount;
Integer prevCObj = clientPastVersionStats.get(cversObj);
prevCount = (prevCObj != null) ? prevCObj.intValue() : 0;
clientPastVersionStats.put(cversObj, Integer.valueOf(1 + prevCount));
// This client version is OK to connect
return true;
}
use of soc.server.genericServer.Connection in project JSettlers2 by jdmonin.
the class SOCServer method destroyGame.
/**
* Destroy a game and clean up related data, such as the owner's count of
* {@link SOCClientData#getCurrentCreatedGames()}.
*<P>
* Note that if this game had the {@link SOCGame#isBotsOnly} flag, and {@link #numRobotOnlyGamesRemaining} > 0,
* will call {@link #startRobotOnlyGames(boolean)}.
*<P>
* <B>Locks:</B> Must have {@link #gameList}{@link SOCGameList#takeMonitor() .takeMonitor()}
* before calling this method.
*
* @param gm Name of the game to destroy
* @see #leaveGame(Connection, String, boolean, boolean)
* @see #destroyGameAndBroadcast(String, String)
*/
public void destroyGame(String gm) {
// D.ebugPrintln("***** destroyGame("+gm+")");
SOCGame cg = null;
cg = gameList.getGameData(gm);
if (cg == null)
return;
final boolean wasBotsOnly = cg.isBotsOnly;
// /
// / write out game data
// /
/*
currentGameEventRecord.setSnapshot(cg);
saveCurrentGameEventRecord(gm);
SOCGameRecord gr = (SOCGameRecord)gameRecords.get(gm);
writeGameRecord(gm, gr);
*/
// /
// / delete the game from gamelist,
// / tell all robots to leave
// /
Vector<Connection> members = null;
members = gameList.getMembers(gm);
// also calls SOCGame.destroyGame
gameList.deleteGame(gm);
if (members != null) {
Enumeration<Connection> conEnum = members.elements();
while (conEnum.hasMoreElements()) {
Connection con = conEnum.nextElement();
con.put(SOCRobotDismiss.toCmd(gm));
}
}
// Reduce the owner's games-active count
final String gaOwner = cg.getOwner();
if (gaOwner != null) {
Connection oConn = conns.get(gaOwner);
if (oConn != null)
((SOCClientData) oConn.getAppData()).deletedGame();
}
if (wasBotsOnly && (numRobotOnlyGamesRemaining > 0))
startRobotOnlyGames(true);
}
use of soc.server.genericServer.Connection in project JSettlers2 by jdmonin.
the class SOCServer method messageToGame.
/**
* Send a message to the given game.
*<P>
* <b>Locks:</b> Takes, releases {@link SOCGameList#takeMonitorForGame(String)}.
*
* @param ga the name of the game
* @param mes the message to send. If mes is a SOCGameTextMsg whose
* text begins with ">>>", the client should consider this
* an urgent message, and draw the user's attention in some way.
* (See {@link #messageToGameUrgent(String, String)})
* @see #messageToGame(String, String)
* @see #messageToGameWithMon(String, SOCMessage)
* @see #messageToGameForVersions(SOCGame, int, int, SOCMessage, boolean)
*/
public void messageToGame(String ga, SOCMessage mes) {
final String mesCmd = mes.toCmd();
gameList.takeMonitorForGame(ga);
try {
Vector<Connection> v = gameList.getMembers(ga);
if (v != null) {
// D.ebugPrintln("M2G - "+mes);
Enumeration<Connection> menum = v.elements();
while (menum.hasMoreElements()) {
Connection c = menum.nextElement();
if (c != null) {
// currentGameEventRecord.addMessageOut(new SOCMessageRecord(mes, "SERVER", c.getData()));
c.put(mesCmd);
}
}
}
} catch (Exception e) {
D.ebugPrintStackTrace(e, "Exception in messageToGame");
}
gameList.releaseMonitorForGame(ga);
}
use of soc.server.genericServer.Connection in project JSettlers2 by jdmonin.
the class SOCServerRobotPinger method run.
/**
* Robot ping loop thread:
* Send a {@link SOCServerPing} to each bot connected to the server,
* then sleep for {@link #sleepTime} minus 60 seconds.
* Exits loop when {@link #stopPinger()} is called.
*/
@Override
public void run() {
final String pingCmdStr = ping.toCmd();
while (alive) {
boolean retry = false;
if (!robotConnections.isEmpty()) {
try {
for (Connection robotConnection : robotConnections) {
if (D.ebugIsEnabled())
D.ebugPrintln("(*)(*)(*)(*) PINGING " + robotConnection.getData());
robotConnection.put(pingCmdStr);
}
} catch (ConcurrentModificationException e) {
retry = true;
}
}
yield();
final int msec = (retry) ? 250 : (sleepTime - 60000);
try {
sleep(msec);
} catch (InterruptedException exc) {
}
}
//
// cleanup
//
robotConnections = null;
}
use of soc.server.genericServer.Connection in project JSettlers2 by jdmonin.
the class SOCServerMessageHandler method handleSITDOWN.
/**
* handle "sit down" message
*
* @param c the connection that sent the message
* @param mes the message
* @since 1.0.0
*/
private void handleSITDOWN(Connection c, SOCSitDown mes) {
if (c == null)
return;
final String gaName = mes.getGame();
SOCGame ga = gameList.getGameData(gaName);
if (ga == null) {
// Out of date client info, or may be observing a deleted game.
// Already authenticated (c != null), so replying is OK by security.
// "Game not found."
srv.messageToPlayerKeyed(c, gaName, "reply.game.not.found");
// <--- Early return: No active game found ---
return;
}
/**
* make sure this player isn't already sitting
*/
boolean canSit = true;
boolean gameIsFull = false, gameAlreadyStarted = false;
/*
for (int i = 0; i < SOCGame.MAXPLAYERS; i++) {
if (ga.getPlayer(i).getName() == c.getData()) {
canSit = false;
break;
}
}
*/
// D.ebugPrintln("ga.isSeatVacant(mes.getPlayerNumber()) = "+ga.isSeatVacant(mes.getPlayerNumber()));
/**
* if this is a robot, remove it from the request list
*/
boolean isBotJoinRequest = false;
{
Hashtable<Connection, Object> joinRequests = srv.robotJoinRequests.get(gaName);
if (joinRequests != null)
isBotJoinRequest = (null != joinRequests.remove(c));
}
/**
* make sure a person isn't sitting here already;
* if a robot is sitting there, dismiss the robot.
* Can't sit at a vacant seat after everyone has
* placed 1st settlement+road (state >= START2A).
*
* If a human leaves after game is started, seat will appear vacant when the
* requested bot sits to replace them, so let the bot sit at that vacant seat.
*/
final int pn = mes.getPlayerNumber();
ga.takeMonitor();
try {
if (ga.isSeatVacant(pn)) {
gameAlreadyStarted = (ga.getGameState() >= SOCGame.START2A);
if (!gameAlreadyStarted)
gameIsFull = (1 > ga.getAvailableSeatCount());
if (gameIsFull || (gameAlreadyStarted && !isBotJoinRequest))
canSit = false;
} else {
SOCPlayer seatedPlayer = ga.getPlayer(pn);
if (seatedPlayer.isRobot() && (ga.getSeatLock(pn) != SOCGame.SeatLockState.LOCKED) && (ga.getCurrentPlayerNumber() != pn)) {
/**
* boot the robot out of the game
*/
Connection robotCon = srv.getConnection(seatedPlayer.getName());
robotCon.put(SOCRobotDismiss.toCmd(gaName));
/**
* this connection has to wait for the robot to leave
* and then it can sit down
*/
Vector<SOCReplaceRequest> disRequests = srv.robotDismissRequests.get(gaName);
SOCReplaceRequest req = new SOCReplaceRequest(c, robotCon, mes);
if (disRequests == null) {
disRequests = new Vector<SOCReplaceRequest>();
disRequests.addElement(req);
srv.robotDismissRequests.put(gaName, disRequests);
} else {
disRequests.addElement(req);
}
}
canSit = false;
}
} catch (Exception e) {
D.ebugPrintStackTrace(e, "Exception caught at handleSITDOWN");
}
ga.releaseMonitor();
// D.ebugPrintln("canSit 2 = "+canSit);
if (canSit) {
srv.sitDown(ga, c, pn, mes.isRobot(), false);
} else {
/**
* if the robot can't sit, tell it to go away.
* otherwise if game is full, tell the player.
*/
if (mes.isRobot()) {
c.put(SOCRobotDismiss.toCmd(gaName));
} else if (gameAlreadyStarted) {
srv.messageToPlayerKeyed(c, gaName, "member.sit.game.started");
// "This game has already started; to play you must take over a robot."
} else if (gameIsFull) {
srv.messageToPlayerKeyed(c, gaName, "member.sit.game.full");
// "This game is full; you cannot sit down."
}
}
}
Aggregations