use of soc.server.genericServer.Connection in project JSettlers2 by jdmonin.
the class SOCServer method checkNickname.
// sendGameList
/**
* Check if a nickname is okay, and, if they're already logged in, whether a
* new replacement connection can "take over" the existing one.
*<P>
* a name is ok if it hasn't been used yet, isn't {@link #SERVERNAME the server's name},
* and (since 1.1.07) passes {@link SOCMessage#isSingleLineAndSafe(String)}.
*<P>
* The "take over" option is used for reconnect when a client loses
* connection, and server doesn't realize it.
* A new connection can "take over" the name after a timeout; check
* the return value.
* (After {@link #NICKNAME_TAKEOVER_SECONDS_SAME_IP} or
* {@link #NICKNAME_TAKEOVER_SECONDS_DIFFERENT_IP} seconds)
* When taking over, the new connection's client version must be able
* to join all games that the old connection is playing, as returned
* by {@link SOCGameListAtServer#playerGamesMinVersion(Connection) gameList.playerGamesMinVersion}.
*
* @param n the name
* @param newc A new incoming connection, asking for this name
* @param withPassword Did the connection supply a password?
* @param isBot True if authenticating as robot, false if human
* @return 0 if the name is okay; <BR>
* -1 if OK <strong>and you are taking over a connection;</strong> <BR>
* -2 if not OK by rules (fails isSingleLineAndSafe,
* named "debug" or {@link #SERVERNAME},
* human with bot name prefix, etc); <BR>
* -vers if not OK by version (for takeover; will be -1000 lower); <BR>
* or, the number of seconds after which <tt>newc</tt> can
* take over this name's games.
* @see #checkNickname_getRetryText(int)
*/
private int checkNickname(String n, Connection newc, final boolean withPassword, final boolean isBot) {
if (n.equals(SERVERNAME)) {
return -2;
}
if (!SOCMessage.isSingleLineAndSafe(n)) {
return -2;
}
// check "debug" and bot name prefixes used in setupLocalRobots
final String nLower = n.toLowerCase(Locale.US);
if ((nLower.equals("debug") && !isDebugUserEnabled()) || ((!isBot) && (nLower.startsWith("droid ") || nLower.startsWith("robot ")))) {
return -2;
}
// check conns hashtable
Connection oldc = getConnection(n, false);
if (oldc == null) {
// OK: no player by that name already
return 0;
}
// Can we take over this one?
SOCClientData scd = (SOCClientData) oldc.getAppData();
if (scd == null) {
// Shouldn't happen; name and SCD are assigned at same time
return -2;
}
final int timeoutNeeded;
if (withPassword)
timeoutNeeded = NICKNAME_TAKEOVER_SECONDS_SAME_PASSWORD;
else if (newc.host().equals(oldc.host()))
// same IP address or hostname
timeoutNeeded = NICKNAME_TAKEOVER_SECONDS_SAME_IP;
else
timeoutNeeded = NICKNAME_TAKEOVER_SECONDS_DIFFERENT_IP;
final long now = System.currentTimeMillis();
if (scd.disconnectLastPingMillis != 0) {
int secondsSincePing = (int) (((now - scd.disconnectLastPingMillis)) / 1000L);
if (secondsSincePing >= timeoutNeeded) {
// Already sent ping, timeout has expired.
// Re-check version just in case.
int minVersForGames = gameList.playerGamesMinVersion(oldc);
if (minVersForGames > newc.getVersion()) {
if (minVersForGames < 1000)
minVersForGames = 1000;
// too old to play
return -minVersForGames;
}
// to nameConnection(c,true) will transfer data from old conn, to new conn.
return -1;
} else {
// Already sent ping, timeout not yet expired.
return timeoutNeeded - secondsSincePing;
}
}
// Have not yet sent a ping.
int minVersForGames = gameList.playerGamesMinVersion(oldc);
if (minVersForGames > newc.getVersion()) {
if (minVersForGames < 1000)
minVersForGames = 1000;
// too old to play
return -minVersForGames;
}
scd.disconnectLastPingMillis = now;
if (oldc.getVersion() >= 1108) {
// Already-connected client should respond to ping.
// If not, consider them disconnected.
oldc.put(SOCServerPing.toCmd(timeoutNeeded));
}
return timeoutNeeded;
}
use of soc.server.genericServer.Connection in project JSettlers2 by jdmonin.
the class SOCServer method readyGameAskRobotsMix3p.
/**
* While readying a game in {@link #readyGameAskRobotsJoin(SOCGame, Connection[], int)},
* adjust the mix of requested bots as needed when third-party bots are wanted.
*<P>
* <B>Note:</B> Currently treats {@code reqPct3p} as a minimum percentage; third-party
* bots are only added to, not removed from, the bots requested for the new game.
*
* @param reqPct3p Requested third-party bot percentage, from {@link #PROP_JSETTLERS_BOTS_PERCENT3P}
* @param robotsRequested Set of randomly-selected bots joining the game; third-party bots may be
* swapped into here. Key = bot Connection, value = seat number as {@link Integer}
* like in {@link #robotJoinRequests}
* @param robotSeatsConns Array of player positions (seats) to be occupied by bots; all non-null elements
* are bots in {@code robotsRequested}; third-party bots may be swapped into here
* @since 2.0.00
*/
private void readyGameAskRobotsMix3p(final int reqPct3p, final Hashtable<Connection, Object> robotsRequested, final Connection[] robotSeatsConns) {
// TODO this algorithm isn't elegant or very efficient
final int numBotsReq = robotsRequested.size();
final int num3pReq = Math.round((numBotsReq * reqPct3p) / 100f);
int curr3pReq = 0;
boolean[] curr3pSeat = new boolean[robotSeatsConns.length];
for (int i = 0; i < robotSeatsConns.length; ++i) {
if ((robotSeatsConns[i] != null) && !((SOCClientData) (robotSeatsConns[i].getAppData())).isBuiltInRobot) {
++curr3pReq;
curr3pSeat[i] = true;
}
}
if (curr3pReq >= num3pReq)
// <--- Early return: Already the right minimum percentage ---
return;
// fill unused3p, the list of 3p bots which aren't already requested and in robotRequests
List<Connection> unused3p;
synchronized (robots3p) {
unused3p = new ArrayList<Connection>(robots3p);
}
for (int i = 0; i < robotSeatsConns.length; ++i) if (curr3pSeat[i])
unused3p.remove(robotSeatsConns[i]);
// use random bots from unused3p in robotRequests and robotSeatsConns:
int nAdd = num3pReq - curr3pReq;
while ((nAdd > 0) && !unused3p.isEmpty()) {
// pick iNon, a non-3p bot seat index to remove
int iNon = -1;
int nSkip = (nAdd > 1) ? rand.nextInt(num3pReq - curr3pReq) : 0;
for (int i = 0; i < robotSeatsConns.length; ++i) {
if ((robotSeatsConns[i] != null) && !curr3pSeat[i]) {
if (nSkip == 0) {
iNon = i;
break;
} else {
--nSkip;
}
}
}
if (iNon == -1)
// <--- Early return: Non-3p bot seat not found ---
return;
// pick bot3p, an unused 3p bot to fill iNon
int s = unused3p.size();
Connection bot3p = unused3p.remove((s > 1) ? rand.nextInt(s) : 0);
// update structures
Integer iObj = Integer.valueOf(iNon);
synchronized (robotsRequested) {
robotsRequested.remove(robotSeatsConns[iNon]);
robotsRequested.put(bot3p, iObj);
}
robotSeatsConns[iNon] = bot3p;
curr3pSeat[iNon] = true;
--nAdd;
}
}
use of soc.server.genericServer.Connection in project JSettlers2 by jdmonin.
the class SOCServer method processDebugCommand.
/**
* Process a debug command, sent by the "debug" client/player.
* Some debug commands are server-wide, some apply to a specific game.
* If no server-wide commands match, server will call
* {@link GameHandler#processDebugCommand(Connection, String, String, String)}
* to check for those.
*<P>
* Check {@link #allowDebugUser} before calling this method.
* For list of commands see {@link #GENERAL_COMMANDS_HELP}, {@link #DEBUG_COMMANDS_HELP},
* {@link #ADMIN_USER_COMMANDS_HELP}, and {@link GameHandler#getDebugCommandsHelp()}.
* "Unprivileged" general commands are handled by
* {@link SOCServerMessageHandler#handleGAMETEXTMSG(Connection, SOCGameTextMsg)}.
*
* @param debugCli Client sending the potential debug command
* @param ga Game in which the message is sent
* @param dcmd Text message which may be a debug command
* @param dcmdU {@code dcmd} as uppercase, for efficiency (it's already been uppercased in caller)
* @return true if {@code dcmd} is a recognized debug command, false otherwise
*/
public boolean processDebugCommand(Connection debugCli, String ga, final String dcmd, final String dcmdU) {
// See SOCServerMessageHandler.handleGAMETEXTMSG for "unprivileged" debug commands like *HELP*, *STATS*, and *ADDTIME*.
// eventual return value; will set false if unrecognized
boolean isCmd = true;
if (dcmdU.startsWith("*KILLGAME*")) {
messageToGameUrgent(ga, ">>> ********** " + debugCli.getData() + " KILLED THE GAME!!! ********** <<<");
destroyGameAndBroadcast(ga, "KILLGAME");
} else if (dcmdU.startsWith("*GC*")) {
Runtime rt = Runtime.getRuntime();
rt.gc();
messageToGame(ga, "> GARBAGE COLLECTING DONE");
messageToGame(ga, "> Free Memory: " + rt.freeMemory());
} else if (// dcmd to force case-sensitivity
dcmd.startsWith("*STOP*")) {
// Extra info needed to shut it down: Server console output
boolean shutNow = false;
final long now = System.currentTimeMillis();
if ((srvShutPassword != null) && (now <= srvShutPasswordExpire)) {
// look for trailing \n, look for shutdown pw preceding it
int end = dcmd.length();
while (Character.isISOControl(dcmd.charAt(end - 1))) --end;
final int i = dcmd.lastIndexOf(' ');
if ((i < dcmd.length()) && (dcmd.substring(i + 1, end).equals(srvShutPassword)))
shutNow = true;
} else {
srvShutPasswordExpire = now + (45 * 1000L);
StringBuffer sb = new StringBuffer();
for (int i = 12 + rand.nextInt(5); i > 0; --i) sb.append((char) (33 + rand.nextInt(126 - 33)));
srvShutPassword = sb.toString();
System.err.println("** Shutdown password generated: " + srvShutPassword);
broadcast(SOCBCastTextMsg.toCmd(debugCli.getData() + " WANTS TO STOP THE SERVER"));
messageToPlayer(debugCli, ga, "Send stop command again with the password.");
}
if (shutNow) {
String stopMsg = ">>> ********** " + debugCli.getData() + " KILLED THE SERVER!!! ********** <<<";
stopServer(stopMsg);
System.exit(0);
}
} else if (dcmdU.startsWith("*BCAST* ")) {
// /
// / broadcast to all chat channels and games
// /
broadcast(SOCBCastTextMsg.toCmd(dcmd.substring(8)));
} else if (dcmdU.startsWith("*BOTLIST*")) {
Enumeration<Connection> robotsEnum = robots.elements();
while (robotsEnum.hasMoreElements()) {
Connection robotConn = robotsEnum.nextElement();
messageToGame(ga, "> Robot: " + robotConn.getData());
robotConn.put(SOCAdminPing.toCmd((ga)));
}
} else if (dcmdU.startsWith("*RESETBOT* ")) {
String botName = dcmd.substring(11).trim();
messageToGame(ga, "> botName = '" + botName + "'");
Enumeration<Connection> robotsEnum = robots.elements();
boolean botFound = false;
while (robotsEnum.hasMoreElements()) {
Connection robotConn = robotsEnum.nextElement();
if (botName.equals(robotConn.getData())) {
botFound = true;
messageToGame(ga, "> SENDING RESET COMMAND TO " + botName);
robotConn.put(new SOCAdminReset().toCmd());
break;
}
}
if (!botFound)
D.ebugPrintln("L2590 Bot not found to reset: " + botName);
} else if (dcmdU.startsWith("*KILLBOT* ")) {
String botName = dcmd.substring(10).trim();
messageToGame(ga, "> botName = '" + botName + "'");
Enumeration<Connection> robotsEnum = robots.elements();
boolean botFound = false;
while (robotsEnum.hasMoreElements()) {
Connection robotConn = robotsEnum.nextElement();
if (botName.equals(robotConn.getData())) {
botFound = true;
messageToGame(ga, "> DISCONNECTING " + botName);
removeConnection(robotConn, true);
break;
}
}
if (!botFound)
D.ebugPrintln("L2614 Bot not found to disconnect: " + botName);
} else if (dcmdU.startsWith("*STARTBOTGAME*")) {
if (0 == getConfigIntProperty(PROP_JSETTLERS_BOTS_BOTGAMES_TOTAL, 0)) {
messageToPlayer(debugCli, ga, "To start a bots-only game, must restart server with " + PROP_JSETTLERS_BOTS_BOTGAMES_TOTAL + " != 0.");
return true;
}
SOCGame gameObj = getGame(ga);
if (gameObj == null)
// we're sitting in this game, shouldn't happen
return true;
if (gameObj.getGameState() != SOCGame.NEW) {
messageToPlayer(debugCli, ga, "This game has already started; you must create a new one.");
return true;
}
int maxBots = 0;
if (dcmdU.length() > 15) {
try {
maxBots = Integer.parseInt(dcmdU.substring(15).trim());
} catch (NumberFormatException e) {
}
}
srvMsgHandler.handleSTARTGAME(debugCli, new SOCStartGame(ga, 0), maxBots);
return true;
} else {
// See if game type's handler finds a debug command
GameHandler hand = gameList.getGameTypeHandler(ga);
if (hand != null)
isCmd = hand.processDebugCommand(debugCli, ga, dcmd, dcmdU);
else
isCmd = false;
}
return isCmd;
}
use of soc.server.genericServer.Connection in project JSettlers2 by jdmonin.
the class SOCServer method messageToChannelWithMon.
/**
* Send a message to the given channel
*
* WARNING: MUST HAVE THE gameList.takeMonitorForChannel(ch) before
* calling this method
*
* @param ch the name of the channel
* @param mes the message to send
*/
public void messageToChannelWithMon(String ch, SOCMessage mes) {
Vector<Connection> v = channelList.getMembers(ch);
if (v != null) {
final String mesCmd = mes.toCmd();
Enumeration<Connection> menum = v.elements();
while (menum.hasMoreElements()) {
Connection c = menum.nextElement();
if (c != null) {
c.put(mesCmd);
}
}
}
}
use of soc.server.genericServer.Connection in project JSettlers2 by jdmonin.
the class SOCServer method messageToChannel.
/**
* Send a message to the given channel
*
* @param ch the name of the channel
* @param mes the message to send
*/
public void messageToChannel(String ch, SOCMessage mes) {
final String mesCmd = mes.toCmd();
channelList.takeMonitorForChannel(ch);
try {
Vector<Connection> v = channelList.getMembers(ch);
if (v != null) {
Enumeration<Connection> menum = v.elements();
while (menum.hasMoreElements()) {
Connection c = menum.nextElement();
if (c != null) {
c.put(mesCmd);
}
}
}
} catch (Exception e) {
D.ebugPrintStackTrace(e, "Exception in messageToChannel");
}
channelList.releaseMonitorForChannel(ch);
}
Aggregations