Search in sources :

Example 16 with Connection

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;
}
Also used : StringConnection(soc.server.genericServer.StringConnection) Connection(soc.server.genericServer.Connection)

Example 17 with Connection

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;
    }
}
Also used : StringConnection(soc.server.genericServer.StringConnection) Connection(soc.server.genericServer.Connection)

Example 18 with Connection

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;
}
Also used : Enumeration(java.util.Enumeration) StringConnection(soc.server.genericServer.StringConnection) Connection(soc.server.genericServer.Connection)

Example 19 with Connection

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);
            }
        }
    }
}
Also used : StringConnection(soc.server.genericServer.StringConnection) Connection(soc.server.genericServer.Connection)

Example 20 with Connection

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);
}
Also used : StringConnection(soc.server.genericServer.StringConnection) Connection(soc.server.genericServer.Connection) MissingResourceException(java.util.MissingResourceException) EOFException(java.io.EOFException) DBSettingMismatchException(soc.server.database.DBSettingMismatchException) SocketException(java.net.SocketException) SQLException(java.sql.SQLException) IOException(java.io.IOException)

Aggregations

Connection (soc.server.genericServer.Connection)44 StringConnection (soc.server.genericServer.StringConnection)24 SQLException (java.sql.SQLException)8 MissingResourceException (java.util.MissingResourceException)8 SOCGame (soc.game.SOCGame)7 EOFException (java.io.EOFException)5 IOException (java.io.IOException)5 SocketException (java.net.SocketException)5 ArrayList (java.util.ArrayList)5 SOCPlayerElement (soc.message.SOCPlayerElement)5 DBSettingMismatchException (soc.server.database.DBSettingMismatchException)5 SOCPlayer (soc.game.SOCPlayer)4 Date (java.util.Date)3 SOCGameElements (soc.message.SOCGameElements)3 HashSet (java.util.HashSet)2 Hashtable (java.util.Hashtable)2 TimerTask (java.util.TimerTask)2 Vector (java.util.Vector)2 SOCDevCardAction (soc.message.SOCDevCardAction)2 SOCInventoryItemAction (soc.message.SOCInventoryItemAction)2