/projects/marauroa-3.8.1/src/marauroa/server/game/container/PlayerEntry.java
https://gitlab.com/essere.lab.public/qualitas.class-corpus · Java · 519 lines · 218 code · 63 blank · 238 comment · 19 complexity · b62c4c44b388090a5d8c5f6e8f907dd3 MD5 · raw file
- /* $Id: PlayerEntry.java,v 1.59 2010/07/04 17:29:02 nhnb Exp $ */
- /***************************************************************************
- * (C) Copyright 2007 - Marauroa *
- ***************************************************************************
- ***************************************************************************
- * *
- * This program is free software; you can redistribute it and/or modify *
- * it under the terms of the GNU General Public License as published by *
- * the Free Software Foundation; either version 2 of the License, or *
- * (at your option) any later version. *
- * *
- ***************************************************************************/
- package marauroa.server.game.container;
- import java.io.IOException;
- import java.net.InetAddress;
- import java.nio.channels.SocketChannel;
- import java.sql.SQLException;
- import java.util.List;
- import marauroa.common.Configuration;
- import marauroa.common.Log4J;
- import marauroa.common.Logger;
- import marauroa.common.TimeoutConf;
- import marauroa.common.Utility;
- import marauroa.common.crypto.RSAKey;
- import marauroa.common.game.RPObject;
- import marauroa.common.net.NetConst;
- import marauroa.common.net.message.Message;
- import marauroa.common.net.message.TransferContent;
- import marauroa.common.net.message.MessageS2CLoginNACK.Reasons;
- import marauroa.server.db.DBTransaction;
- import marauroa.server.db.TransactionPool;
- import marauroa.server.db.command.DBCommand;
- import marauroa.server.db.command.DBCommandQueue;
- import marauroa.server.game.db.AccountDAO;
- import marauroa.server.game.db.CharacterDAO;
- import marauroa.server.game.db.DAORegister;
- import marauroa.server.game.db.LoginEventDAO;
- import marauroa.server.game.dbcommand.StoreCharacterCommand;
- /**
- * This class represent a player on game. It handles all the business glue that
- * it is needed by the server.
- *
- * @author miguel
- */
- public class PlayerEntry {
- /**
- * This class stores the information needed to allow a secure login. Once
- * login is completed the information is cleared.
- */
- public static class SecuredLoginInfo {
- private static Logger logger = Log4J.getLogger(SecuredLoginInfo.class);
- /** A long array of bytes that represent the Hash of a random value. */
- public byte[] serverNonce;
- /** A long array of bytes that represent a random value. */
- public byte[] clientNonce;
- /** A long byte array that represent the hash of the client Nonce field */
- public byte[] clientNonceHash;
- /** Username of the player */
- public String username;
- /**
- * An array that represent the hash of the password xor ClientNonce xor
- * ServerNonce.
- */
- public byte[] password;
- /** The server RSA key. */
- public RSAKey key;
- /** client ip address */
- public InetAddress address;
- /** seed identifying the client */
- public String seed;
- /** reason why a login failed */
- public Reasons reason;
- /**
- * Constructor
- *
- * @param key
- * the server private key
- * @param clientNonceHash
- * the client hash
- * @param serverNonce
- * the server random bigint
- * @param address client ip address
- */
- public SecuredLoginInfo(RSAKey key, byte[] clientNonceHash, byte[] serverNonce, InetAddress address) {
- this.key = key;
- this.clientNonceHash = Utility.copy(clientNonceHash);
- this.serverNonce = Utility.copy(serverNonce);
- this.address = address;
- }
- /**
- * Verify that a player is whom he/she says it is.
- *
- * @return true if it is correct: username and password matches.
- * @throws SQLException
- * if there is any database problem.
- */
- public boolean verify() throws SQLException {
- return DAORegister.get().get(AccountDAO.class).verify(this);
- }
- /**
- * Add a login event to database each time player login, even if it
- * fails.
- *
- * @param address
- * the IP address that originated the request.
- * @param loginResult
- * the result of the login action, where true is login
- * correct and false login failed.
- * @throws SQLException
- * if there is any database problem.
- */
- public void addLoginEvent(InetAddress address, boolean loginResult) 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(username, address, service, seed, loginResult);
- }
- /**
- * 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;
- }
- /**
- * Returns true if an account is temporarily blocked due to too many
- * tries in the defined time frame.
- *
- * @return true if an account is temporarily blocked due to too many
- * tries in the defined time frame.
- * @throws SQLException
- * if there is any database problem.
- */
- public boolean isBlocked() throws SQLException {
- DBTransaction transaction = TransactionPool.get().beginWork();
- boolean res = true;
- try {
- LoginEventDAO loginEventDAO = DAORegister.get().get(LoginEventDAO.class);
- res = loginEventDAO.isAccountBlocked(transaction, username)
- || loginEventDAO.isAddressBlocked(transaction, address.getHostAddress());
-
- TransactionPool.get().commit(transaction);
- } catch (SQLException e) {
- TransactionPool.get().rollback(transaction);
- logger.error(e, e);
- }
- return res;
- }
- /**
- * Returns a string indicating the status of the account.
- * It can be: <ul>
- * <li>active
- * <li>inactive
- * <li>banned
- * </ul>
- * @return a string indicating the status of the account.
- * @throws SQLException
- */
- public String getStatus() throws SQLException {
- DBTransaction transaction = TransactionPool.get().beginWork();
- String res = null;
- try {
- if (DAORegister.get().get(AccountDAO.class).hasPlayer(transaction, username)) {
- res = DAORegister.get().get(AccountDAO.class).getAccountBanMessage(transaction, username);
- }
- TransactionPool.get().commit(transaction);
- } catch (SQLException e) {
- TransactionPool.get().rollback(transaction);
- }
- return res;
- }
- }
- /**
- * We record when this player entry was created to remove players that don't
- * complete login stage but that keep connected.
- */
- public long creationTime;
- /** The state in which this player is */
- public ClientState state;
- /** The runtime clientid */
- public int clientid;
- /** The client associated SocketChannel */
- public SocketChannel channel;
- /**
- * The login Info. It is created after the first login message and destroyed
- * after the login is finished.
- */
- public SecuredLoginInfo loginInformations;
- /** The name of the player */
- public String username;
- /** The name of the choosen character */
- public String character;
- /** The object of the player */
- public RPObject object;
-
- /**
- * We need to control if player is active because sometimes server changes its IP
- * and we are not able to realize about this at server side, so all the clients are left there
- * until the TCP stack determine a timeout that can be a long time.
- */
- public long activityTimestamp;
-
- /**
- * We define how many milliseconds has to be elapsed until we consider a player has timeout.
- */
- private static final long TIMEOUT_IN_GAME_MILLISECONDS= 30 * 1000;
- /**
- * We need a longer timeout pre-game because players might want to create a character here,
- * there is no keep-alive yet.
- */
- private static final long TIMEOUT_PRE_GAME_MILLISECONDS= 10 * 60 * 1000;
-
- /**
- * A counter to detect dropped packets or bad order at client side. We
- * enumerate each perception so client can know in which order it is
- * expected to apply them. When using TCP there is no problem as delivery is
- * guaranted.
- */
- public int perceptionCounter;
- /** It is true if client notified us that it got out of sync */
- public boolean requestedSync;
- /** Contains the content that is going to be transfered to client */
- public List<TransferContent> contentToTransfer;
- /** version of protocol this client speaks */
- private int protocolVersion = NetConst.NETWORK_PROTOCOL_VERSION;
- /**
- * Constructor
- *
- * @param channel
- * the socket channel
- */
- public PlayerEntry(SocketChannel channel) {
- this.channel = channel;
- clientid = Message.CLIENTID_INVALID;
- state = ClientState.CONNECTION_ACCEPTED;
- loginInformations = null;
- username = null;
- character = null;
- object = null;
- perceptionCounter = 0;
- /*
- * We set this to true so that RP Manager will send a sync perception to
- * player as soon as possible.
- */
- requestedSync = true;
- contentToTransfer = null;
-
- creationTime = System.currentTimeMillis();
- activityTimestamp=creationTime;
- }
- /**
- * Return the inet address of this PlayerEntry.
- *
- * @return the inet address of this PlayerEntry.
- */
- public InetAddress getAddress() {
- return channel.socket().getInetAddress();
- }
-
- /**
- * Returns true when nothing has been received from client in TIMEOUT_SECONDS.
- * Note that client sends confirmations to perceptions, so this mean that client is
- * for whatever reason not working.
- *
- * @return true when nothing has been received from client in TIMEOUT_SECONDS.
- */
- public boolean isTimeout() {
- if (state==ClientState.GAME_BEGIN) {
- return (System.currentTimeMillis()-activityTimestamp)>TIMEOUT_IN_GAME_MILLISECONDS;
- } else {
- return (System.currentTimeMillis()-activityTimestamp)>TIMEOUT_PRE_GAME_MILLISECONDS;
- }
- }
-
- /**
- * Refresh player timeout timestamp.
- * This method is invoked when a new message arrives from client.
- */
- public void update() {
- activityTimestamp=System.currentTimeMillis();
- }
- /**
- * Returns the next perception timestamp.
- *
- * @return the next perception timestamp
- */
- public int getPerceptionTimestamp() {
- return perceptionCounter++;
- }
- /**
- * returns the current perception timestamp
- *
- * @return the current perception timestamp
- */
- public int getThisPerceptionTimestamp() {
- return perceptionCounter;
- }
- /**
- * Clears the contents to be transfered
- */
- public void clearContent() {
- contentToTransfer = null;
- }
- /**
- * Returns the named content or returns null if it is not found.
- *
- * @param name
- * name of the content to find
- * @return the content or null if it is not found.
- */
- public TransferContent getContent(String name) {
- if (contentToTransfer == null) {
- return null;
- }
- for (TransferContent item : contentToTransfer) {
- if (item.name.equals(name)) {
- return item;
- }
- }
- return null;
- }
- /**
- * This method stores an object at database backend
- *
- * @param player
- * the object to store
- * @throws SQLException in case of an database error
- * @throws IOException in case of an input/output error
- */
- public void storeRPObject(RPObject player) throws SQLException, IOException {
- // And update the entry
- object = player;
- // We store the object in the database
- DBCommand command = new StoreCharacterCommand(username, character, player);
- DBCommandQueue.get().enqueue(command);
- }
- /**
- * This method query database to check if the player with username given by
- * the entry has a character with the name passed as argument.
- *
- * @param character
- * The name we are querying for.
- * @return true if it is found or false otherwise.
- * @throws SQLException
- * If there is a Database exception.
- */
- public boolean hasCharacter(String character) throws SQLException {
- return DAORegister.get().get(CharacterDAO.class).hasCharacter(username, character);
- }
- /**
- * Allows to ban this player account
- * @throws SQLException
- */
- public void ban() throws SQLException {
- DAORegister.get().get(AccountDAO.class).setAccountStatus(username, "banned");
- }
-
- /**
- * sets the RPObject for this entry.
- *
- * @param object RPObject
- */
- public void setObject(RPObject object) {
- this.object = object;
- }
- /**
- * This method returns a list of all the characters available for this
- * player
- *
- * @return a list containing all the usable characters
- * @throws SQLException
- * if there is any database problem.
- */
- public List<String> getCharacters() throws SQLException {
- return DAORegister.get().get(CharacterDAO.class).getCharacters(username);
- }
- /**
- * This method forces an update on the next perception sending.
- */
- public void requestSync() {
- requestedSync = true;
- }
- /**
- * Return a list of the previous login attempts.
- *
- * @return a list of the previous login attempts.
- * @throws SQLException
- */
- public List<String> getPreviousLogins() throws SQLException {
- return DAORegister.get().get(LoginEventDAO.class).getLoginEvents(username, 1);
- }
- /**
- * This method tag this entry as removable if there is more than
- * UNCOMPLETED_LOGIN_TIMEOUT milliseconds since the creation time of the
- * entry and the actual time and the entry has not completed the login
- * stage.
- *
- * @return true, if it is removable, false otherwise
- */
- boolean isRemovable() {
- /*
- * Add logged players that didn't choose a character or that have not
- * even login yet.
- */
- boolean isInOKState = (state == ClientState.GAME_BEGIN);
- return !isInOKState
- && System.currentTimeMillis() - creationTime > TimeoutConf.UNCOMPLETED_LOGIN_TIMEOUT;
- }
- /**
- * gets the version of the protocol this clients speaks
- *
- * @return protocolVersion
- */
- public int getProtocolVersion() {
- return protocolVersion;
- }
- /**
- * sets the protocol version
- *
- * @param protocolVersion protocol version
- */
- public void setProtocolVersion(int protocolVersion) {
- this.protocolVersion = Math.min(NetConst.NETWORK_PROTOCOL_VERSION, protocolVersion);
- }
- @Override
- public String toString() {
- StringBuffer os = new StringBuffer("PlayerEntry");
- os.append("[clientid=" + clientid + "]");
- os.append("[channel=" + channel + "]");
- os.append("[state=" + state + "]");
- os.append("[username=" + username + "]");
- os.append("[character=" + character + "]");
- os.append("[object defined=" + (object != null) + "]");
- return os.toString();
- }
- }