/**
   * 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);
  }
Example #2
0
	/**
	 * 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;
    }
  }