PageRenderTime 280ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/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
  1. /* $Id: PlayerEntry.java,v 1.59 2010/07/04 17:29:02 nhnb Exp $ */
  2. /***************************************************************************
  3. * (C) Copyright 2007 - Marauroa *
  4. ***************************************************************************
  5. ***************************************************************************
  6. * *
  7. * This program is free software; you can redistribute it and/or modify *
  8. * it under the terms of the GNU General Public License as published by *
  9. * the Free Software Foundation; either version 2 of the License, or *
  10. * (at your option) any later version. *
  11. * *
  12. ***************************************************************************/
  13. package marauroa.server.game.container;
  14. import java.io.IOException;
  15. import java.net.InetAddress;
  16. import java.nio.channels.SocketChannel;
  17. import java.sql.SQLException;
  18. import java.util.List;
  19. import marauroa.common.Configuration;
  20. import marauroa.common.Log4J;
  21. import marauroa.common.Logger;
  22. import marauroa.common.TimeoutConf;
  23. import marauroa.common.Utility;
  24. import marauroa.common.crypto.RSAKey;
  25. import marauroa.common.game.RPObject;
  26. import marauroa.common.net.NetConst;
  27. import marauroa.common.net.message.Message;
  28. import marauroa.common.net.message.TransferContent;
  29. import marauroa.common.net.message.MessageS2CLoginNACK.Reasons;
  30. import marauroa.server.db.DBTransaction;
  31. import marauroa.server.db.TransactionPool;
  32. import marauroa.server.db.command.DBCommand;
  33. import marauroa.server.db.command.DBCommandQueue;
  34. import marauroa.server.game.db.AccountDAO;
  35. import marauroa.server.game.db.CharacterDAO;
  36. import marauroa.server.game.db.DAORegister;
  37. import marauroa.server.game.db.LoginEventDAO;
  38. import marauroa.server.game.dbcommand.StoreCharacterCommand;
  39. /**
  40. * This class represent a player on game. It handles all the business glue that
  41. * it is needed by the server.
  42. *
  43. * @author miguel
  44. */
  45. public class PlayerEntry {
  46. /**
  47. * This class stores the information needed to allow a secure login. Once
  48. * login is completed the information is cleared.
  49. */
  50. public static class SecuredLoginInfo {
  51. private static Logger logger = Log4J.getLogger(SecuredLoginInfo.class);
  52. /** A long array of bytes that represent the Hash of a random value. */
  53. public byte[] serverNonce;
  54. /** A long array of bytes that represent a random value. */
  55. public byte[] clientNonce;
  56. /** A long byte array that represent the hash of the client Nonce field */
  57. public byte[] clientNonceHash;
  58. /** Username of the player */
  59. public String username;
  60. /**
  61. * An array that represent the hash of the password xor ClientNonce xor
  62. * ServerNonce.
  63. */
  64. public byte[] password;
  65. /** The server RSA key. */
  66. public RSAKey key;
  67. /** client ip address */
  68. public InetAddress address;
  69. /** seed identifying the client */
  70. public String seed;
  71. /** reason why a login failed */
  72. public Reasons reason;
  73. /**
  74. * Constructor
  75. *
  76. * @param key
  77. * the server private key
  78. * @param clientNonceHash
  79. * the client hash
  80. * @param serverNonce
  81. * the server random bigint
  82. * @param address client ip address
  83. */
  84. public SecuredLoginInfo(RSAKey key, byte[] clientNonceHash, byte[] serverNonce, InetAddress address) {
  85. this.key = key;
  86. this.clientNonceHash = Utility.copy(clientNonceHash);
  87. this.serverNonce = Utility.copy(serverNonce);
  88. this.address = address;
  89. }
  90. /**
  91. * Verify that a player is whom he/she says it is.
  92. *
  93. * @return true if it is correct: username and password matches.
  94. * @throws SQLException
  95. * if there is any database problem.
  96. */
  97. public boolean verify() throws SQLException {
  98. return DAORegister.get().get(AccountDAO.class).verify(this);
  99. }
  100. /**
  101. * Add a login event to database each time player login, even if it
  102. * fails.
  103. *
  104. * @param address
  105. * the IP address that originated the request.
  106. * @param loginResult
  107. * the result of the login action, where true is login
  108. * correct and false login failed.
  109. * @throws SQLException
  110. * if there is any database problem.
  111. */
  112. public void addLoginEvent(InetAddress address, boolean loginResult) throws SQLException {
  113. String service = null;
  114. try {
  115. Configuration conf = Configuration.getConfiguration();
  116. if (conf.has("server_service")) {
  117. service = conf.get("server_service");
  118. } else {
  119. service = conf.get("server_typeGame");
  120. }
  121. } catch (IOException e) {
  122. logger.error(e, e);
  123. }
  124. DAORegister.get().get(LoginEventDAO.class).addLoginEvent(username, address, service, seed, loginResult);
  125. }
  126. /**
  127. * counts the number of connections from this ip-address
  128. *
  129. * @param playerContainer PlayerEntryContainer
  130. * @return number of active connections
  131. */
  132. public int countConnectionsFromSameIPAddress(PlayerEntryContainer playerContainer) {
  133. if (address == null) {
  134. return 0;
  135. }
  136. int counter = 0;
  137. for (PlayerEntry playerEntry : playerContainer) {
  138. try {
  139. if ((playerEntry.getAddress() != null) && address.getHostAddress().equals(playerEntry.getAddress().getHostAddress())) {
  140. counter++;
  141. }
  142. } catch (NullPointerException e) {
  143. logger.error(address);
  144. logger.error(address.getHostAddress());
  145. logger.error(playerEntry);
  146. logger.error(playerEntry);
  147. logger.error(playerEntry.getAddress());
  148. logger.error(e, e);
  149. }
  150. }
  151. return counter;
  152. }
  153. /**
  154. * Returns true if an account is temporarily blocked due to too many
  155. * tries in the defined time frame.
  156. *
  157. * @return true if an account is temporarily blocked due to too many
  158. * tries in the defined time frame.
  159. * @throws SQLException
  160. * if there is any database problem.
  161. */
  162. public boolean isBlocked() throws SQLException {
  163. DBTransaction transaction = TransactionPool.get().beginWork();
  164. boolean res = true;
  165. try {
  166. LoginEventDAO loginEventDAO = DAORegister.get().get(LoginEventDAO.class);
  167. res = loginEventDAO.isAccountBlocked(transaction, username)
  168. || loginEventDAO.isAddressBlocked(transaction, address.getHostAddress());
  169. TransactionPool.get().commit(transaction);
  170. } catch (SQLException e) {
  171. TransactionPool.get().rollback(transaction);
  172. logger.error(e, e);
  173. }
  174. return res;
  175. }
  176. /**
  177. * Returns a string indicating the status of the account.
  178. * It can be: <ul>
  179. * <li>active
  180. * <li>inactive
  181. * <li>banned
  182. * </ul>
  183. * @return a string indicating the status of the account.
  184. * @throws SQLException
  185. */
  186. public String getStatus() throws SQLException {
  187. DBTransaction transaction = TransactionPool.get().beginWork();
  188. String res = null;
  189. try {
  190. if (DAORegister.get().get(AccountDAO.class).hasPlayer(transaction, username)) {
  191. res = DAORegister.get().get(AccountDAO.class).getAccountBanMessage(transaction, username);
  192. }
  193. TransactionPool.get().commit(transaction);
  194. } catch (SQLException e) {
  195. TransactionPool.get().rollback(transaction);
  196. }
  197. return res;
  198. }
  199. }
  200. /**
  201. * We record when this player entry was created to remove players that don't
  202. * complete login stage but that keep connected.
  203. */
  204. public long creationTime;
  205. /** The state in which this player is */
  206. public ClientState state;
  207. /** The runtime clientid */
  208. public int clientid;
  209. /** The client associated SocketChannel */
  210. public SocketChannel channel;
  211. /**
  212. * The login Info. It is created after the first login message and destroyed
  213. * after the login is finished.
  214. */
  215. public SecuredLoginInfo loginInformations;
  216. /** The name of the player */
  217. public String username;
  218. /** The name of the choosen character */
  219. public String character;
  220. /** The object of the player */
  221. public RPObject object;
  222. /**
  223. * We need to control if player is active because sometimes server changes its IP
  224. * and we are not able to realize about this at server side, so all the clients are left there
  225. * until the TCP stack determine a timeout that can be a long time.
  226. */
  227. public long activityTimestamp;
  228. /**
  229. * We define how many milliseconds has to be elapsed until we consider a player has timeout.
  230. */
  231. private static final long TIMEOUT_IN_GAME_MILLISECONDS= 30 * 1000;
  232. /**
  233. * We need a longer timeout pre-game because players might want to create a character here,
  234. * there is no keep-alive yet.
  235. */
  236. private static final long TIMEOUT_PRE_GAME_MILLISECONDS= 10 * 60 * 1000;
  237. /**
  238. * A counter to detect dropped packets or bad order at client side. We
  239. * enumerate each perception so client can know in which order it is
  240. * expected to apply them. When using TCP there is no problem as delivery is
  241. * guaranted.
  242. */
  243. public int perceptionCounter;
  244. /** It is true if client notified us that it got out of sync */
  245. public boolean requestedSync;
  246. /** Contains the content that is going to be transfered to client */
  247. public List<TransferContent> contentToTransfer;
  248. /** version of protocol this client speaks */
  249. private int protocolVersion = NetConst.NETWORK_PROTOCOL_VERSION;
  250. /**
  251. * Constructor
  252. *
  253. * @param channel
  254. * the socket channel
  255. */
  256. public PlayerEntry(SocketChannel channel) {
  257. this.channel = channel;
  258. clientid = Message.CLIENTID_INVALID;
  259. state = ClientState.CONNECTION_ACCEPTED;
  260. loginInformations = null;
  261. username = null;
  262. character = null;
  263. object = null;
  264. perceptionCounter = 0;
  265. /*
  266. * We set this to true so that RP Manager will send a sync perception to
  267. * player as soon as possible.
  268. */
  269. requestedSync = true;
  270. contentToTransfer = null;
  271. creationTime = System.currentTimeMillis();
  272. activityTimestamp=creationTime;
  273. }
  274. /**
  275. * Return the inet address of this PlayerEntry.
  276. *
  277. * @return the inet address of this PlayerEntry.
  278. */
  279. public InetAddress getAddress() {
  280. return channel.socket().getInetAddress();
  281. }
  282. /**
  283. * Returns true when nothing has been received from client in TIMEOUT_SECONDS.
  284. * Note that client sends confirmations to perceptions, so this mean that client is
  285. * for whatever reason not working.
  286. *
  287. * @return true when nothing has been received from client in TIMEOUT_SECONDS.
  288. */
  289. public boolean isTimeout() {
  290. if (state==ClientState.GAME_BEGIN) {
  291. return (System.currentTimeMillis()-activityTimestamp)>TIMEOUT_IN_GAME_MILLISECONDS;
  292. } else {
  293. return (System.currentTimeMillis()-activityTimestamp)>TIMEOUT_PRE_GAME_MILLISECONDS;
  294. }
  295. }
  296. /**
  297. * Refresh player timeout timestamp.
  298. * This method is invoked when a new message arrives from client.
  299. */
  300. public void update() {
  301. activityTimestamp=System.currentTimeMillis();
  302. }
  303. /**
  304. * Returns the next perception timestamp.
  305. *
  306. * @return the next perception timestamp
  307. */
  308. public int getPerceptionTimestamp() {
  309. return perceptionCounter++;
  310. }
  311. /**
  312. * returns the current perception timestamp
  313. *
  314. * @return the current perception timestamp
  315. */
  316. public int getThisPerceptionTimestamp() {
  317. return perceptionCounter;
  318. }
  319. /**
  320. * Clears the contents to be transfered
  321. */
  322. public void clearContent() {
  323. contentToTransfer = null;
  324. }
  325. /**
  326. * Returns the named content or returns null if it is not found.
  327. *
  328. * @param name
  329. * name of the content to find
  330. * @return the content or null if it is not found.
  331. */
  332. public TransferContent getContent(String name) {
  333. if (contentToTransfer == null) {
  334. return null;
  335. }
  336. for (TransferContent item : contentToTransfer) {
  337. if (item.name.equals(name)) {
  338. return item;
  339. }
  340. }
  341. return null;
  342. }
  343. /**
  344. * This method stores an object at database backend
  345. *
  346. * @param player
  347. * the object to store
  348. * @throws SQLException in case of an database error
  349. * @throws IOException in case of an input/output error
  350. */
  351. public void storeRPObject(RPObject player) throws SQLException, IOException {
  352. // And update the entry
  353. object = player;
  354. // We store the object in the database
  355. DBCommand command = new StoreCharacterCommand(username, character, player);
  356. DBCommandQueue.get().enqueue(command);
  357. }
  358. /**
  359. * This method query database to check if the player with username given by
  360. * the entry has a character with the name passed as argument.
  361. *
  362. * @param character
  363. * The name we are querying for.
  364. * @return true if it is found or false otherwise.
  365. * @throws SQLException
  366. * If there is a Database exception.
  367. */
  368. public boolean hasCharacter(String character) throws SQLException {
  369. return DAORegister.get().get(CharacterDAO.class).hasCharacter(username, character);
  370. }
  371. /**
  372. * Allows to ban this player account
  373. * @throws SQLException
  374. */
  375. public void ban() throws SQLException {
  376. DAORegister.get().get(AccountDAO.class).setAccountStatus(username, "banned");
  377. }
  378. /**
  379. * sets the RPObject for this entry.
  380. *
  381. * @param object RPObject
  382. */
  383. public void setObject(RPObject object) {
  384. this.object = object;
  385. }
  386. /**
  387. * This method returns a list of all the characters available for this
  388. * player
  389. *
  390. * @return a list containing all the usable characters
  391. * @throws SQLException
  392. * if there is any database problem.
  393. */
  394. public List<String> getCharacters() throws SQLException {
  395. return DAORegister.get().get(CharacterDAO.class).getCharacters(username);
  396. }
  397. /**
  398. * This method forces an update on the next perception sending.
  399. */
  400. public void requestSync() {
  401. requestedSync = true;
  402. }
  403. /**
  404. * Return a list of the previous login attempts.
  405. *
  406. * @return a list of the previous login attempts.
  407. * @throws SQLException
  408. */
  409. public List<String> getPreviousLogins() throws SQLException {
  410. return DAORegister.get().get(LoginEventDAO.class).getLoginEvents(username, 1);
  411. }
  412. /**
  413. * This method tag this entry as removable if there is more than
  414. * UNCOMPLETED_LOGIN_TIMEOUT milliseconds since the creation time of the
  415. * entry and the actual time and the entry has not completed the login
  416. * stage.
  417. *
  418. * @return true, if it is removable, false otherwise
  419. */
  420. boolean isRemovable() {
  421. /*
  422. * Add logged players that didn't choose a character or that have not
  423. * even login yet.
  424. */
  425. boolean isInOKState = (state == ClientState.GAME_BEGIN);
  426. return !isInOKState
  427. && System.currentTimeMillis() - creationTime > TimeoutConf.UNCOMPLETED_LOGIN_TIMEOUT;
  428. }
  429. /**
  430. * gets the version of the protocol this clients speaks
  431. *
  432. * @return protocolVersion
  433. */
  434. public int getProtocolVersion() {
  435. return protocolVersion;
  436. }
  437. /**
  438. * sets the protocol version
  439. *
  440. * @param protocolVersion protocol version
  441. */
  442. public void setProtocolVersion(int protocolVersion) {
  443. this.protocolVersion = Math.min(NetConst.NETWORK_PROTOCOL_VERSION, protocolVersion);
  444. }
  445. @Override
  446. public String toString() {
  447. StringBuffer os = new StringBuffer("PlayerEntry");
  448. os.append("[clientid=" + clientid + "]");
  449. os.append("[channel=" + channel + "]");
  450. os.append("[state=" + state + "]");
  451. os.append("[username=" + username + "]");
  452. os.append("[character=" + character + "]");
  453. os.append("[object defined=" + (object != null) + "]");
  454. return os.toString();
  455. }
  456. }