L2GameClient.java 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608
  1. /*
  2. * This program is free software: you can redistribute it and/or modify it under
  3. * the terms of the GNU General Public License as published by the Free Software
  4. * Foundation, either version 3 of the License, or (at your option) any later
  5. * version.
  6. *
  7. * This program is distributed in the hope that it will be useful, but WITHOUT
  8. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  9. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  10. * details.
  11. *
  12. * You should have received a copy of the GNU General Public License along with
  13. * this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. package net.sf.l2j.gameserver.network;
  16. import java.net.InetAddress;
  17. import java.nio.ByteBuffer;
  18. import java.sql.PreparedStatement;
  19. import java.util.List;
  20. import java.util.concurrent.RejectedExecutionException;
  21. import java.util.concurrent.ScheduledFuture;
  22. import java.util.concurrent.locks.ReentrantLock;
  23. import java.util.logging.Level;
  24. import java.util.logging.Logger;
  25. import javolution.util.FastList;
  26. import net.sf.l2j.Config;
  27. import net.sf.l2j.L2DatabaseFactory;
  28. import net.sf.l2j.gameserver.LoginServerThread;
  29. import net.sf.l2j.gameserver.ThreadPoolManager;
  30. import net.sf.l2j.gameserver.LoginServerThread.SessionKey;
  31. import net.sf.l2j.gameserver.communitybbs.Manager.RegionBBSManager;
  32. import net.sf.l2j.gameserver.datatables.SkillTable;
  33. import net.sf.l2j.gameserver.model.CharSelectInfoPackage;
  34. import net.sf.l2j.gameserver.model.L2World;
  35. import net.sf.l2j.gameserver.model.actor.instance.L2PcInstance;
  36. import net.sf.l2j.gameserver.model.entity.L2Event;
  37. import net.sf.l2j.gameserver.serverpackets.L2GameServerPacket;
  38. import net.sf.l2j.util.EventData;
  39. import org.mmocore.network.MMOClient;
  40. import org.mmocore.network.MMOConnection;
  41. /**
  42. * Represents a client connected on Game Server
  43. * @author KenM
  44. */
  45. public final class L2GameClient extends MMOClient<MMOConnection<L2GameClient>>
  46. {
  47. protected static final Logger _log = Logger.getLogger(L2GameClient.class.getName());
  48. /**
  49. * CONNECTED - client has just connected
  50. * AUTHED - client has authed but doesnt has character attached to it yet
  51. * IN_GAME - client has selected a char and is in game
  52. * @author KenM
  53. */
  54. public static enum GameClientState { CONNECTED, AUTHED, IN_GAME };
  55. public GameClientState state;
  56. // Info
  57. private String _accountName;
  58. private SessionKey _sessionId;
  59. private L2PcInstance _activeChar;
  60. private ReentrantLock _activeCharLock = new ReentrantLock();
  61. private boolean _isAuthedGG;
  62. private long _connectionStartTime;
  63. private List<Integer> _charSlotMapping = new FastList<Integer>();
  64. // Task
  65. protected final ScheduledFuture<?> _autoSaveInDB;
  66. // Crypt
  67. private GameCrypt _crypt;
  68. // Flood protection
  69. public byte packetsSentInSec = 0;
  70. public int packetsSentStartTick = 0;
  71. public L2GameClient(MMOConnection<L2GameClient> con)
  72. {
  73. super(con);
  74. state = GameClientState.CONNECTED;
  75. _connectionStartTime = System.currentTimeMillis();
  76. _crypt = new GameCrypt();
  77. if (Config.CHAR_STORE_INTERVAL > 0)
  78. {
  79. _autoSaveInDB = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(
  80. new AutoSaveTask(), 300000L, (Config.CHAR_STORE_INTERVAL*60000L)
  81. );
  82. }
  83. else
  84. {
  85. _autoSaveInDB = null;
  86. }
  87. }
  88. public byte[] enableCrypt()
  89. {
  90. byte[] key = BlowFishKeygen.getRandomKey();
  91. _crypt.setKey(key);
  92. return key;
  93. }
  94. public GameClientState getState()
  95. {
  96. return state;
  97. }
  98. public void setState(GameClientState pState)
  99. {
  100. state = pState;
  101. }
  102. public long getConnectionStartTime()
  103. {
  104. return _connectionStartTime;
  105. }
  106. @Override
  107. public boolean decrypt(ByteBuffer buf, int size)
  108. {
  109. _crypt.decrypt(buf.array(), buf.position(), size);
  110. return true;
  111. }
  112. @Override
  113. public boolean encrypt(final ByteBuffer buf, final int size)
  114. {
  115. _crypt.encrypt(buf.array(), buf.position(), size);
  116. buf.position(buf.position() + size);
  117. return true;
  118. }
  119. public L2PcInstance getActiveChar()
  120. {
  121. return _activeChar;
  122. }
  123. public void setActiveChar(L2PcInstance pActiveChar)
  124. {
  125. _activeChar = pActiveChar;
  126. if (_activeChar != null)
  127. {
  128. L2World.getInstance().storeObject(getActiveChar());
  129. }
  130. }
  131. public ReentrantLock getActiveCharLock()
  132. {
  133. return _activeCharLock;
  134. }
  135. public void setGameGuardOk(boolean val)
  136. {
  137. _isAuthedGG = val;
  138. }
  139. public boolean isAuthedGG()
  140. {
  141. return _isAuthedGG;
  142. }
  143. public void setAccountName(String pAccountName)
  144. {
  145. _accountName = pAccountName;
  146. }
  147. public String getAccountName()
  148. {
  149. return _accountName;
  150. }
  151. public void setSessionId(SessionKey sk)
  152. {
  153. _sessionId = sk;
  154. }
  155. public SessionKey getSessionId()
  156. {
  157. return _sessionId;
  158. }
  159. public void sendPacket(L2GameServerPacket gsp)
  160. {
  161. getConnection().sendPacket(gsp);
  162. gsp.runImpl();
  163. }
  164. public L2PcInstance markToDeleteChar(int charslot) throws Exception
  165. {
  166. //have to make sure active character must be nulled
  167. /*if (getActiveChar() != null)
  168. {
  169. saveCharToDisk(getActiveChar());
  170. if (Config.DEBUG)
  171. {
  172. _log.fine("active Char saved");
  173. }
  174. this.setActiveChar(null);
  175. }*/
  176. int objid = getObjectIdForSlot(charslot);
  177. if (objid < 0)
  178. return null;
  179. L2PcInstance character = L2PcInstance.load(objid);
  180. if (character.getClanId() != 0)
  181. return character;
  182. character.deleteMe();
  183. java.sql.Connection con = null;
  184. try
  185. {
  186. con = L2DatabaseFactory.getInstance().getConnection();
  187. PreparedStatement statement = con.prepareStatement("UPDATE characters SET deletetime=? WHERE obj_Id=?");
  188. statement.setLong(1, System.currentTimeMillis() + Config.DELETE_DAYS*86400000L); // 24*60*60*1000 = 86400000
  189. statement.setInt(2, objid);
  190. statement.execute();
  191. statement.close();
  192. }
  193. catch (Exception e)
  194. {
  195. _log.warning("Data error on update delete time of char: " + e);
  196. }
  197. finally
  198. {
  199. try { con.close(); } catch (Exception e) {}
  200. }
  201. return null;
  202. }
  203. public L2PcInstance deleteChar(int charslot) throws Exception
  204. {
  205. //have to make sure active character must be nulled
  206. /*if (getActiveChar() != null)
  207. {
  208. saveCharToDisk (getActiveChar());
  209. if (Config.DEBUG) _log.fine("active Char saved");
  210. this.setActiveChar(null);
  211. }*/
  212. int objid = getObjectIdForSlot(charslot);
  213. if (objid < 0)
  214. return null;
  215. L2PcInstance character = L2PcInstance.load(objid);
  216. if (character.getClanId() != 0)
  217. return character;
  218. character.deleteMe();
  219. deleteCharByObjId(objid);
  220. return null;
  221. }
  222. /**
  223. * Save the L2PcInstance to the database.
  224. */
  225. public static void saveCharToDisk(L2PcInstance cha)
  226. {
  227. try
  228. {
  229. cha.store();
  230. if (Config.UPDATE_ITEMS_ON_CHAR_STORE)
  231. {
  232. cha.getInventory().updateDatabase();
  233. }
  234. }
  235. catch(Exception e)
  236. {
  237. _log.severe("Error saving player character: "+e);
  238. }
  239. }
  240. public void markRestoredChar(int charslot) throws Exception
  241. {
  242. //have to make sure active character must be nulled
  243. /*if (getActiveChar() != null)
  244. {
  245. saveCharToDisk (getActiveChar());
  246. if (Config.DEBUG) _log.fine("active Char saved");
  247. this.setActiveChar(null);
  248. }*/
  249. int objid = getObjectIdForSlot(charslot);
  250. if (objid < 0)
  251. return;
  252. java.sql.Connection con = null;
  253. try
  254. {
  255. con = L2DatabaseFactory.getInstance().getConnection();
  256. PreparedStatement statement = con.prepareStatement("UPDATE characters SET deletetime=0 WHERE obj_Id=?");
  257. statement.setInt(1, objid);
  258. statement.execute();
  259. statement.close();
  260. }
  261. catch (Exception e)
  262. {
  263. _log.severe("Data error on restoring char: " + e);
  264. }
  265. finally
  266. {
  267. try { con.close(); } catch (Exception e) {}
  268. }
  269. }
  270. public static void deleteCharByObjId(int objid)
  271. {
  272. if (objid < 0)
  273. return;
  274. java.sql.Connection con = null;
  275. try
  276. {
  277. con = L2DatabaseFactory.getInstance().getConnection();
  278. PreparedStatement statement ;
  279. statement = con.prepareStatement("DELETE FROM character_friends WHERE char_id=? OR friend_id=?");
  280. statement.setInt(1, objid);
  281. statement.setInt(2, objid);
  282. statement.execute();
  283. statement.close();
  284. statement = con.prepareStatement("DELETE FROM character_hennas WHERE char_obj_id=?");
  285. statement.setInt(1, objid);
  286. statement.execute();
  287. statement.close();
  288. statement = con.prepareStatement("DELETE FROM character_macroses WHERE char_obj_id=?");
  289. statement.setInt(1, objid);
  290. statement.execute();
  291. statement.close();
  292. statement = con.prepareStatement("DELETE FROM character_quests WHERE char_id=?");
  293. statement.setInt(1, objid);
  294. statement.execute();
  295. statement.close();
  296. statement = con.prepareStatement("DELETE FROM character_recipebook WHERE char_id=?");
  297. statement.setInt(1, objid);
  298. statement.execute();
  299. statement.close();
  300. statement = con.prepareStatement("DELETE FROM character_shortcuts WHERE char_obj_id=?");
  301. statement.setInt(1, objid);
  302. statement.execute();
  303. statement.close();
  304. statement = con.prepareStatement("DELETE FROM character_skills WHERE char_obj_id=?");
  305. statement.setInt(1, objid);
  306. statement.execute();
  307. statement.close();
  308. statement = con.prepareStatement("DELETE FROM character_skills_save WHERE char_obj_id=?");
  309. statement.setInt(1, objid);
  310. statement.execute();
  311. statement.close();
  312. statement = con.prepareStatement("DELETE FROM character_subclasses WHERE char_obj_id=?");
  313. statement.setInt(1, objid);
  314. statement.execute();
  315. statement.close();
  316. statement = con.prepareStatement("DELETE FROM heroes WHERE char_id=?");
  317. statement.setInt(1, objid);
  318. statement.execute();
  319. statement.close();
  320. statement = con.prepareStatement("DELETE FROM olympiad_nobles WHERE char_id=?");
  321. statement.setInt(1, objid);
  322. statement.execute();
  323. statement.close();
  324. statement = con.prepareStatement("DELETE FROM seven_signs WHERE char_obj_id=?");
  325. statement.setInt(1, objid);
  326. statement.execute();
  327. statement.close();
  328. statement = con.prepareStatement("DELETE FROM pets WHERE item_obj_id IN (SELECT object_id FROM items WHERE items.owner_id=?)");
  329. statement.setInt(1, objid);
  330. statement.execute();
  331. statement.close();
  332. statement = con.prepareStatement("DELETE FROM augmentations WHERE item_id IN (SELECT object_id FROM items WHERE items.owner_id=?)");
  333. statement.setInt(1, objid);
  334. statement.execute();
  335. statement.close();
  336. statement = con.prepareStatement("DELETE FROM items WHERE owner_id=?");
  337. statement.setInt(1, objid);
  338. statement.execute();
  339. statement.close();
  340. statement = con.prepareStatement("DELETE FROM merchant_lease WHERE player_id=?");
  341. statement.setInt(1, objid);
  342. statement.execute();
  343. statement.close();
  344. statement = con.prepareStatement("DELETE FROM characters WHERE obj_Id=?");
  345. statement.setInt(1, objid);
  346. statement.execute();
  347. statement.close();
  348. }
  349. catch (Exception e)
  350. {
  351. _log.warning("Data error on deleting char: " + e);
  352. }
  353. finally
  354. {
  355. try { con.close(); } catch (Exception e) {}
  356. }
  357. }
  358. public L2PcInstance loadCharFromDisk(int charslot)
  359. {
  360. L2PcInstance character = L2PcInstance.load(getObjectIdForSlot(charslot));
  361. if (character != null)
  362. {
  363. // preinit some values for each login
  364. character.setRunning(); // running is default
  365. character.standUp(); // standing is default
  366. character.refreshOverloaded();
  367. character.refreshExpertisePenalty();
  368. character.setOnlineStatus(true);
  369. }
  370. else
  371. {
  372. _log.severe("could not restore in slot: "+ charslot);
  373. }
  374. //setCharacter(character);
  375. return character;
  376. }
  377. /**
  378. * @param chars
  379. */
  380. public void setCharSelection(CharSelectInfoPackage[] chars)
  381. {
  382. _charSlotMapping.clear();
  383. for (int i = 0; i < chars.length; i++)
  384. {
  385. int objectId = chars[i].getObjectId();
  386. _charSlotMapping.add(new Integer(objectId));
  387. }
  388. }
  389. public void close(L2GameServerPacket gsp)
  390. {
  391. getConnection().close(gsp);
  392. }
  393. /**
  394. * @param charslot
  395. * @return
  396. */
  397. private int getObjectIdForSlot(int charslot)
  398. {
  399. if (charslot < 0 || charslot >= _charSlotMapping.size())
  400. {
  401. _log.warning(toString()+" tried to delete Character in slot "+charslot+" but no characters exits at that slot.");
  402. return -1;
  403. }
  404. Integer objectId = _charSlotMapping.get(charslot);
  405. return objectId.intValue();
  406. }
  407. @Override
  408. protected void onForcedDisconnection()
  409. {
  410. _log.info("Client "+toString()+" disconnected abnormally.");
  411. }
  412. @Override
  413. protected void onDisconnection()
  414. {
  415. // no long running tasks here, do it async
  416. try
  417. {
  418. ThreadPoolManager.getInstance().executeTask(new DisconnectTask());
  419. }
  420. catch (RejectedExecutionException e)
  421. {
  422. // server is closing
  423. }
  424. }
  425. /**
  426. * Produces the best possible string representation of this client.
  427. */
  428. @Override
  429. public String toString()
  430. {
  431. try
  432. {
  433. InetAddress address = getConnection().getSocket().getInetAddress();
  434. switch (getState())
  435. {
  436. case CONNECTED:
  437. return "[IP: "+(address == null ? "disconnected" : address.getHostAddress())+"]";
  438. case AUTHED:
  439. return "[Account: "+getAccountName()+" - IP: "+(address == null ? "disconnected" : address.getHostAddress())+"]";
  440. case IN_GAME:
  441. return "[Character: "+(getActiveChar() == null ? "disconnected" : getActiveChar().getName())+" - Account: "+getAccountName()+" - IP: "+(address == null ? "disconnected" : address.getHostAddress())+"]";
  442. default:
  443. throw new IllegalStateException("Missing state on switch");
  444. }
  445. }
  446. catch (NullPointerException e)
  447. {
  448. return "[Character read failed due to disconnect]";
  449. }
  450. }
  451. class DisconnectTask implements Runnable
  452. {
  453. /**
  454. * @see java.lang.Runnable#run()
  455. */
  456. public void run()
  457. {
  458. try
  459. {
  460. // Update BBS
  461. try
  462. {
  463. RegionBBSManager.getInstance().changeCommunityBoard();
  464. }
  465. catch (Exception e)
  466. {
  467. e.printStackTrace();
  468. }
  469. // we are going to mannually save the char bellow thus we can force the cancel
  470. if (_autoSaveInDB != null)
  471. {
  472. _autoSaveInDB.cancel(true);
  473. }
  474. L2PcInstance player = L2GameClient.this.getActiveChar();
  475. if (player != null) // this should only happen on connection loss
  476. {
  477. // we store all data from players who are disconnected while in an event in order to restore it in the next login
  478. if (player.atEvent)
  479. {
  480. EventData data = new EventData(player.eventX, player.eventY, player.eventZ, player.eventkarma, player.eventpvpkills, player.eventpkkills, player.eventTitle, player.kills, player.eventSitForced);
  481. L2Event.connectionLossData.put(player.getName(), data);
  482. }
  483. if (player.isFlying())
  484. {
  485. player.removeSkill(SkillTable.getInstance().getInfo(4289, 1));
  486. }
  487. // notify the world about our disconnect
  488. player.deleteMe();
  489. try
  490. {
  491. saveCharToDisk(player);
  492. }
  493. catch (Exception e2) { /* ignore any problems here */ }
  494. }
  495. L2GameClient.this.setActiveChar(null);
  496. }
  497. catch (Exception e1)
  498. {
  499. _log.log(Level.WARNING, "error while disconnecting client", e1);
  500. }
  501. finally
  502. {
  503. LoginServerThread.getInstance().sendLogout(L2GameClient.this.getAccountName());
  504. }
  505. }
  506. }
  507. class AutoSaveTask implements Runnable
  508. {
  509. public void run()
  510. {
  511. try
  512. {
  513. L2PcInstance player = L2GameClient.this.getActiveChar();
  514. if (player != null)
  515. {
  516. saveCharToDisk(player);
  517. }
  518. }
  519. catch (Throwable e)
  520. {
  521. _log.severe(e.toString());
  522. }
  523. }
  524. }
  525. }