/** * Creates a character for a account of a player * * @param username player's username * @param character * @param template the template we are going to use to create the object. * @param address ip address of client * @return a Result indicating if account creation was done successfully or if it is not the * cause. */ public CharacterResult createCharacter( String username, String character, RPObject template, String address) { try { if (!Boolean.parseBoolean( Configuration.getConfiguration().get("allow_account_creation", "true"))) { return new CharacterResult(Result.FAILED_CREATE_ON_MAIN_INSTEAD, character, template); } } catch (IOException e) { logger.error(e, e); } // check account creation limits try { if (DAORegister.get() .get(CharacterDAO.class) .isCharacterCreationLimitReached(username, address)) { return new CharacterResult(Result.FAILED_TOO_MANY, character, template); } } catch (SQLException e) { logger.error(e, e); return new CharacterResult(Result.FAILED_EXCEPTION, character, template); } catch (IOException e) { logger.error(e, e); return new CharacterResult(Result.FAILED_EXCEPTION, character, template); } return ruleProcessor.createCharacter(username, character, template); }
/** * reads a file into a map * * @param is InputStream * @param resultMap content is store in this map */ private static void readFile(InputStream is, Map<String, String> resultMap) { BufferedReader reader = null; try { reader = new BufferedReader(new InputStreamReader(is, "UTF-8")); String line = reader.readLine(); while (line != null) { int pos = line.indexOf("="); if (pos > -1) { String key = line.substring(0, pos); String value = line.substring(pos + 1); resultMap.put(key, value); } line = reader.readLine(); } } catch (IOException e) { logger.error(e, e); } if (reader != null) { try { reader.close(); } catch (IOException e) { logger.error(e, e); } } }
private void buildPerceptions() { playersToRemove.clear(); /** We reset the cache at Perceptions */ MessageS2CPerception.clearPrecomputedPerception(); for (PlayerEntry entry : playerContainer) { try { // Before creating the perception we check the player is still there. if (entry.isTimeout()) { logger.info("Request (TIMEOUT) disconnection of Player " + entry.getAddress()); playersToRemove.add(entry); continue; } if (entry.state == ClientState.GAME_BEGIN) { Perception perception = getPlayerPerception(entry); sendPlayerPerception(entry, perception, entry.object); } } catch (Exception e) { logger.error( "Removing player(" + entry.clientid + ") because it caused a Exception while contacting it", e); playersToRemove.add(entry); } } for (PlayerEntry entry : playersToRemove) { logger.warn("RP Disconnecting entry: " + entry); netMan.disconnectClient(entry.channel); } }
/** * Adds an action for the next turn * * @param object the object that casted the action * @param action the action itself * @throws ActionInvalidException */ public void addRPAction(RPObject object, RPAction action) throws ActionInvalidException { if (logger.isDebugEnabled()) { logger.debug("Added action: " + action); } scheduler.addRPAction(object, action, ruleProcessor); }
/** * Creates an account for a player in the game. * * @param username player's username * @param password player's password * @param email player's email * @param address ip address of client * @return a Result indicating if account creation was done successfully or not. */ public AccountResult createAccount( String username, String password, String email, String address) { try { if (!Boolean.parseBoolean( Configuration.getConfiguration().get("allow_account_creation", "true"))) { return new AccountResult(Result.FAILED_CREATE_ON_MAIN_INSTEAD, username); } } catch (IOException e) { logger.error(e, e); } // check account creation limits try { if (DAORegister.get().get(AccountDAO.class).isAccountCreationLimitReached(address)) { return new AccountResult(Result.FAILED_TOO_MANY, username); } } catch (SQLException e) { logger.error(e, e); return new AccountResult(Result.FAILED_EXCEPTION, username); } catch (IOException e) { logger.error(e, e); return new AccountResult(Result.FAILED_EXCEPTION, username); } // forward the creation request to the game return ruleProcessor.createAccount(username, password, email); }
private void sendPlayerPerception( PlayerEntry entry, Perception perception, RPObject playerObject) { if (perception == null) { /** Until player enters game perception is null */ return; } MessageS2CPerception messages2cPerception = new MessageS2CPerception(entry.channel, perception); stats.add("Perceptions " + (perception.type == 0 ? "DELTA" : "SYNC"), 1); /* * The perception is build of two parts: the general information and the * private information about our object. This private information * consists only of attributes that are not visible to every player but * the owner, because visible attributes are already stored in the * perception. */ if (perception.type == Perception.SYNC) { RPObject copy = new RPObject(); copy.fill(playerObject); if (!playerObject.isHidden()) { copy.clearVisible(true); } messages2cPerception.setMyRPObject(copy, null); } else { RPObject added = new RPObject(); RPObject deleted = new RPObject(); try { playerObject.getDifferences(added, deleted); if (!playerObject.isHidden()) { added.clearVisible(false); deleted.clearVisible(false); } if (added.size() == 0) { added = null; } if (deleted.size() == 0) { deleted = null; } } catch (Exception e) { logger.error("Error getting object differences", e); logger.error(playerObject); added = null; deleted = null; } messages2cPerception.setMyRPObject(added, deleted); } messages2cPerception.setClientID(entry.clientid); messages2cPerception.setPerceptionTimestamp(entry.getPerceptionTimestamp()); messages2cPerception.setProtocolVersion(entry.getProtocolVersion()); netMan.sendMessage(messages2cPerception); }
/** * gets the decrypted password * * @return the decrypted password hash */ public byte[] getDecryptedPasswordHash() { byte[] b1 = key.decodeByteArray(password); byte[] b2 = Hash.xor(clientNonce, serverNonce); if (b2 == null) { logger.debug("B2 is null"); return null; } byte[] passwordHash = Hash.xor(b1, b2); if (password == null) { logger.debug("Password is null"); return null; } return passwordHash; }
/** * Constructor * * @param netMan the NetworkServerManager so that we can send message * @throws Exception in case of an unexpected error */ public RPServerManager(INetworkServerManager netMan) throws Exception { super("RPServerManager"); try { stats = Statistics.getStatistics(); keepRunning = true; isfinished = false; scheduler = new RPScheduler(); contentsToTransfer = new HashMap<RPObject, List<TransferContent>>(); playerContainer = PlayerEntryContainer.getContainer(); playersToRemove = new LinkedList<PlayerEntry>(); this.netMan = netMan; Configuration conf = Configuration.getConfiguration(); /* * This method loads the extensions that implement the game server * code. */ initializeExtensions(conf); String duration = conf.get("turn_length"); turnDuration = Long.parseLong(duration); turn = 0; } catch (Exception e) { logger.warn("ABORT: Unable to create RPZone, RPRuleProcessor or RPAIManager instances", e); throw e; } }
/** * Takes the given RPObject as the new RPObject representing the user's character. Does nothing if * the provided RPObject isn't the user's character object as determined by * isCharacter(newCharacter) or the provided RPObject is the same as the current one. * * @param newCharacter the new RPObject representing the users character */ public void setCharacter(final RPObject newCharacter) { if (isCharacter(newCharacter) && this.myCharacter != newCharacter) { myCharacter = newCharacter; name = newCharacter.get("name"); } else { log.warn("Character not set because it's not our character."); } }
private void deliverTransferContent() { synchronized (contentsToTransfer) { for (Map.Entry<RPObject, List<TransferContent>> val : contentsToTransfer.entrySet()) { RPObject target = val.getKey(); List<TransferContent> content = val.getValue(); PlayerEntry entry = playerContainer.get(target); if (entry == null) { logger.warn( "Entry for player (" + target + ") does not exist: " + playerContainer, new Throwable()); continue; } if (content == null) { logger.warn("content is null"); } if (!entry.contentToTransfer.isEmpty()) { // prevent DoS if the client never confirms the Transfer offer if (entry.contentToTransfer.size() > 30) { synchronized (entry.contentToTransfer) { for (int i = 0; i < 10; i++) { entry.contentToTransfer.remove(0); } } } logger.warn( "Adding to existing contentToTransfer for player " + entry.character + " old: " + entry.contentToTransfer + " added " + content); } entry.contentToTransfer.addAll(content); MessageS2CTransferREQ mes = new MessageS2CTransferREQ(entry.channel, content); mes.setClientID(entry.clientid); mes.setProtocolVersion(entry.getProtocolVersion()); netMan.sendMessage(mes); } contentsToTransfer.clear(); } }
/** * counts the number of connections from this ip-address * * @param playerContainer PlayerEntryContainer * @return number of active connections */ public int countConnectionsFromSameIPAddress(PlayerEntryContainer playerContainer) { if (address == null) { return 0; } int counter = 0; for (PlayerEntry playerEntry : playerContainer) { try { if ((playerEntry.getAddress() != null) && address.getHostAddress().equals(playerEntry.getAddress().getHostAddress())) { counter++; } } catch (NullPointerException e) { logger.error(address); logger.error(address.getHostAddress()); logger.error(playerEntry); logger.error(playerEntry); logger.error(playerEntry.getAddress()); logger.error(e, e); } } return counter; }
/** * This method finishes the thread that runs the RPServerManager. It calls the RPWorld.onFinish() * method. */ public void finish() { keepRunning = false; while (!isfinished) { Thread.yield(); } try { world.onFinish(); } catch (Exception e) { logger.error("error while finishing RPServerManager", e); } }
@Override public void readObject(marauroa.common.net.InputSerializer in) throws IOException { super.readObject(in); try { reason = Result.values()[in.readByte()]; } catch (ArrayIndexOutOfBoundsException e) { logger.error(e, e); reason = Result.FAILED_EXCEPTION; } if (type != MessageType.S2C_CREATEACCOUNT_NACK) { throw new IOException(); } }
/** * This method disconnects a player from the server. * * @param object the player object that we want to disconnect from world */ public void disconnectPlayer(RPObject object) { PlayerEntry entry = playerContainer.get(object); if (entry == null) { /* * There is no player entry for such channel This is not necesaryly * an error, as the connection could be anything else but an arianne * client or we are just disconnecting a player that logout * correctly. */ logger.warn("There is no PlayerEntry associated to this RPObject."); return; } netMan.disconnectClient(entry.channel); }
/** * This method creates the real connection to database. * * @param connInfo connection information like url, username and password * @return a connection to the database * @throws DatabaseConnectionException if the connection cannot be established. */ protected Connection createConnection(Properties connInfo) throws DatabaseConnectionException { try { // instantiate the Driver class try { if (connInfo.get("jdbc_class") != null) { Class.forName((String) connInfo.get("jdbc_class")).newInstance(); } } catch (Exception e) { throw new DatabaseConnectionException( "Cannot load driver class " + connInfo.get("jdbc_class"), e); } Properties connectionInfo = new Properties(); if (connInfo.get("jdbc_user") != null) { connectionInfo.put("user", connInfo.get("jdbc_user")); } if (connInfo.get("jdbc_pwd") != null) { connectionInfo.put("password", connInfo.get("jdbc_pwd")); } connectionInfo.put("charSet", "UTF-8"); Connection conn = DriverManager.getConnection((String) connInfo.get("jdbc_url"), connectionInfo); // enable transaction support conn.setAutoCommit(false); conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); DatabaseMetaData meta = conn.getMetaData(); logger.info( "Connected to " + connInfo.get("jdbc_url") + ": " + meta.getDatabaseProductName() + " " + meta.getDatabaseProductVersion() + " with driver " + meta.getDriverName() + " " + meta.getDriverVersion()); return conn; } catch (SQLException e) { throw new DatabaseConnectionException( "Unable to create a connection to: " + connInfo.get("jdbc_url"), e); } }
private void savePlayersPeriodicly() { for (PlayerEntry entry : playerContainer) { try { // do not use = 0 because we need a little time until the // player object is fully initialized (e. g. has a charname) if (entry.getThisPerceptionTimestamp() % 2000 == 1999) { entry.storeRPObject(entry.object); } } catch (Exception e) { String name = "null"; if (entry != null) { name = entry.character; } logger.error("Error while storing player " + name, e); } } }
/** * Add a login event to database each time player login, even if it fails. * * @param transaction DBTransactions * @param address the IP address that originated the request. * @param result 0 failed password, 1 successful login, 2 banned, 3 inactive, 4 blocked, 5 merged * @throws SQLException if there is any database problem. */ public void addLoginEvent(DBTransaction transaction, InetAddress address, int result) throws SQLException { String service = null; try { Configuration conf = Configuration.getConfiguration(); if (conf.has("server_service")) { service = conf.get("server_service"); } else { service = conf.get("server_typeGame"); } } catch (IOException e) { logger.error(e, e); } DAORegister.get() .get(LoginEventDAO.class) .addLoginEvent(transaction, username, address, service, seed, result); }
@Override public void run() { try { long start = System.nanoTime(); long stop; long delay; long timeStart = 0; long[] timeEnds = new long[12]; while (keepRunning) { stop = System.nanoTime(); logger.debug("Turn time elapsed: " + ((stop - start) / 1000) + " microsecs"); delay = turnDuration - ((stop - start) / 1000000); if (delay < 0) { StringBuilder sb = new StringBuilder(); for (long timeEnd : timeEnds) { sb.append(" " + (timeEnd - timeStart)); } logger.warn("Turn duration overflow by " + (-delay) + " ms: " + sb.toString()); } else if (delay > turnDuration) { logger.error( "Delay bigger than Turn duration. [delay: " + delay + "] [turnDuration:" + turnDuration + "]"); delay = 0; } // only sleep when the turn delay is > 0 if (delay > 0) { try { Thread.sleep(delay); } catch (InterruptedException e) { // ignore } } start = System.nanoTime(); timeStart = System.currentTimeMillis(); playerContainer.getLock().requestWriteLock(); try { timeEnds[0] = System.currentTimeMillis(); /* Get actions that players send */ scheduler.nextTurn(); timeEnds[1] = System.currentTimeMillis(); /* Execute them all */ scheduler.visit(ruleProcessor); timeEnds[2] = System.currentTimeMillis(); /* Compute game RP rules to move to the next turn */ ruleProcessor.endTurn(); timeEnds[3] = System.currentTimeMillis(); /* Send content that is waiting to players */ deliverTransferContent(); timeEnds[4] = System.currentTimeMillis(); /* Tell player what happened */ buildPerceptions(); timeEnds[5] = System.currentTimeMillis(); /* save players regularly to the db */ savePlayersPeriodicly(); timeEnds[6] = System.currentTimeMillis(); /* Move zone to the next turn */ world.nextTurn(); timeEnds[7] = System.currentTimeMillis(); turn++; ruleProcessor.beginTurn(); timeEnds[8] = System.currentTimeMillis(); } finally { playerContainer.getLock().releaseLock(); timeEnds[9] = System.currentTimeMillis(); } try { stats.set("Objects now", world.size()); } catch (ConcurrentModificationException e) { // TODO: size is obviously not threadsafe as it asks the underlying zone.objects for its // sizes, which are not threadsafe. } timeEnds[10] = System.currentTimeMillis(); TransactionPool.get().kickHangingTransactionsOfThisThread(); timeEnds[11] = System.currentTimeMillis(); } } catch (Throwable e) { logger.error("Unhandled exception, server will shut down.", e); } finally { isfinished = true; } }