L2GameClient.java 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179
  1. /*
  2. * Copyright (C) 2004-2014 L2J Server
  3. *
  4. * This file is part of L2J Server.
  5. *
  6. * L2J Server is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * L2J Server is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. package com.l2jserver.gameserver.network;
  20. import java.net.InetAddress;
  21. import java.net.UnknownHostException;
  22. import java.nio.ByteBuffer;
  23. import java.sql.Connection;
  24. import java.sql.PreparedStatement;
  25. import java.sql.ResultSet;
  26. import java.util.concurrent.ArrayBlockingQueue;
  27. import java.util.concurrent.Future;
  28. import java.util.concurrent.RejectedExecutionException;
  29. import java.util.concurrent.ScheduledFuture;
  30. import java.util.concurrent.locks.ReentrantLock;
  31. import java.util.logging.Level;
  32. import java.util.logging.LogRecord;
  33. import java.util.logging.Logger;
  34. import org.mmocore.network.MMOClient;
  35. import org.mmocore.network.MMOConnection;
  36. import org.mmocore.network.ReceivablePacket;
  37. import com.l2jserver.Config;
  38. import com.l2jserver.L2DatabaseFactory;
  39. import com.l2jserver.gameserver.LoginServerThread;
  40. import com.l2jserver.gameserver.LoginServerThread.SessionKey;
  41. import com.l2jserver.gameserver.ThreadPoolManager;
  42. import com.l2jserver.gameserver.datatables.CharNameTable;
  43. import com.l2jserver.gameserver.datatables.ClanTable;
  44. import com.l2jserver.gameserver.datatables.SecondaryAuthData;
  45. import com.l2jserver.gameserver.instancemanager.AntiFeedManager;
  46. import com.l2jserver.gameserver.model.CharSelectInfoPackage;
  47. import com.l2jserver.gameserver.model.L2Clan;
  48. import com.l2jserver.gameserver.model.L2World;
  49. import com.l2jserver.gameserver.model.PcCondOverride;
  50. import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
  51. import com.l2jserver.gameserver.model.entity.L2Event;
  52. import com.l2jserver.gameserver.model.olympiad.OlympiadManager;
  53. import com.l2jserver.gameserver.model.zone.ZoneId;
  54. import com.l2jserver.gameserver.network.serverpackets.ActionFailed;
  55. import com.l2jserver.gameserver.network.serverpackets.L2GameServerPacket;
  56. import com.l2jserver.gameserver.network.serverpackets.ServerClose;
  57. import com.l2jserver.gameserver.security.SecondaryPasswordAuth;
  58. import com.l2jserver.gameserver.util.FloodProtectors;
  59. import com.l2jserver.gameserver.util.Util;
  60. /**
  61. * Represents a client connected on Game Server.
  62. * @author KenM
  63. */
  64. public final class L2GameClient extends MMOClient<MMOConnection<L2GameClient>> implements Runnable
  65. {
  66. protected static final Logger _log = Logger.getLogger(L2GameClient.class.getName());
  67. protected static final Logger _logAccounting = Logger.getLogger("accounting");
  68. /**
  69. * @author KenM
  70. */
  71. public static enum GameClientState
  72. {
  73. /** Client has just connected . */
  74. CONNECTED,
  75. /** Client has authed but doesn't has character attached to it yet. */
  76. AUTHED,
  77. /** Client has selected a char and is in game. */
  78. IN_GAME
  79. }
  80. private GameClientState _state;
  81. // Info
  82. private final InetAddress _addr;
  83. private String _accountName;
  84. private SessionKey _sessionId;
  85. private L2PcInstance _activeChar;
  86. private final ReentrantLock _activeCharLock = new ReentrantLock();
  87. private SecondaryPasswordAuth _secondaryAuth;
  88. private boolean _isAuthedGG;
  89. private final long _connectionStartTime;
  90. private CharSelectInfoPackage[] _charSlotMapping = null;
  91. // flood protectors
  92. private final FloodProtectors _floodProtectors = new FloodProtectors(this);
  93. // Task
  94. protected final ScheduledFuture<?> _autoSaveInDB;
  95. protected ScheduledFuture<?> _cleanupTask = null;
  96. private L2GameServerPacket _aditionalClosePacket;
  97. // Crypt
  98. private final GameCrypt _crypt;
  99. private final ClientStats _stats;
  100. private boolean _isDetached = false;
  101. private boolean _protocol;
  102. private final ArrayBlockingQueue<ReceivablePacket<L2GameClient>> _packetQueue;
  103. private final ReentrantLock _queueLock = new ReentrantLock();
  104. private int[][] trace;
  105. public L2GameClient(MMOConnection<L2GameClient> con)
  106. {
  107. super(con);
  108. _state = GameClientState.CONNECTED;
  109. _connectionStartTime = System.currentTimeMillis();
  110. _crypt = new GameCrypt();
  111. _stats = new ClientStats();
  112. _packetQueue = new ArrayBlockingQueue<>(Config.CLIENT_PACKET_QUEUE_SIZE);
  113. if (Config.CHAR_STORE_INTERVAL > 0)
  114. {
  115. _autoSaveInDB = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new AutoSaveTask(), 300000L, (Config.CHAR_STORE_INTERVAL * 60000L));
  116. }
  117. else
  118. {
  119. _autoSaveInDB = null;
  120. }
  121. try
  122. {
  123. _addr = con != null ? con.getInetAddress() : InetAddress.getLocalHost();
  124. }
  125. catch (UnknownHostException e)
  126. {
  127. throw new Error("Unable to determine localhost address.");
  128. }
  129. }
  130. public byte[] enableCrypt()
  131. {
  132. byte[] key = BlowFishKeygen.getRandomKey();
  133. _crypt.setKey(key);
  134. return key;
  135. }
  136. public GameClientState getState()
  137. {
  138. return _state;
  139. }
  140. public void setState(GameClientState pState)
  141. {
  142. if (_state != pState)
  143. {
  144. _state = pState;
  145. _packetQueue.clear();
  146. }
  147. }
  148. public ClientStats getStats()
  149. {
  150. return _stats;
  151. }
  152. /**
  153. * For loaded offline traders returns localhost address.
  154. * @return cached connection IP address, for checking detached clients.
  155. */
  156. public InetAddress getConnectionAddress()
  157. {
  158. return _addr;
  159. }
  160. public long getConnectionStartTime()
  161. {
  162. return _connectionStartTime;
  163. }
  164. @Override
  165. public boolean decrypt(ByteBuffer buf, int size)
  166. {
  167. _crypt.decrypt(buf.array(), buf.position(), size);
  168. return true;
  169. }
  170. @Override
  171. public boolean encrypt(final ByteBuffer buf, final int size)
  172. {
  173. _crypt.encrypt(buf.array(), buf.position(), size);
  174. buf.position(buf.position() + size);
  175. return true;
  176. }
  177. public L2PcInstance getActiveChar()
  178. {
  179. return _activeChar;
  180. }
  181. public void setActiveChar(L2PcInstance pActiveChar)
  182. {
  183. _activeChar = pActiveChar;
  184. // JIV remove - done on spawn
  185. /*
  186. * if (_activeChar != null) { L2World.getInstance().storeObject(getActiveChar()); }
  187. */
  188. }
  189. public ReentrantLock getActiveCharLock()
  190. {
  191. return _activeCharLock;
  192. }
  193. public FloodProtectors getFloodProtectors()
  194. {
  195. return _floodProtectors;
  196. }
  197. public void setGameGuardOk(boolean val)
  198. {
  199. _isAuthedGG = val;
  200. }
  201. public boolean isAuthedGG()
  202. {
  203. return _isAuthedGG;
  204. }
  205. public void setAccountName(String pAccountName)
  206. {
  207. _accountName = pAccountName;
  208. if (SecondaryAuthData.getInstance().isEnabled())
  209. {
  210. _secondaryAuth = new SecondaryPasswordAuth(this);
  211. }
  212. }
  213. public String getAccountName()
  214. {
  215. return _accountName;
  216. }
  217. public void setSessionId(SessionKey sk)
  218. {
  219. _sessionId = sk;
  220. }
  221. public SessionKey getSessionId()
  222. {
  223. return _sessionId;
  224. }
  225. public void sendPacket(L2GameServerPacket gsp)
  226. {
  227. if (_isDetached || (gsp == null))
  228. {
  229. return;
  230. }
  231. // Packets from invisible chars sends only to GMs
  232. if (gsp.isInvisible() && (getActiveChar() != null) && !getActiveChar().canOverrideCond(PcCondOverride.SEE_ALL_PLAYERS))
  233. {
  234. return;
  235. }
  236. getConnection().sendPacket(gsp);
  237. gsp.runImpl();
  238. }
  239. public boolean isDetached()
  240. {
  241. return _isDetached;
  242. }
  243. public void setDetached(boolean b)
  244. {
  245. _isDetached = b;
  246. }
  247. /**
  248. * Method to handle character deletion
  249. * @param charslot
  250. * @return a byte: <li>-1: Error: No char was found for such charslot, caught exception, etc... <li>0: character is not member of any clan, proceed with deletion <li>1: character is member of a clan, but not clan leader <li>2: character is clan leader
  251. */
  252. public byte markToDeleteChar(int charslot)
  253. {
  254. int objid = getObjectIdForSlot(charslot);
  255. if (objid < 0)
  256. {
  257. return -1;
  258. }
  259. try (Connection con = L2DatabaseFactory.getInstance().getConnection();
  260. PreparedStatement statement = con.prepareStatement("SELECT clanId FROM characters WHERE charId=?"))
  261. {
  262. statement.setInt(1, objid);
  263. byte answer = 0;
  264. try (ResultSet rs = statement.executeQuery())
  265. {
  266. int clanId = rs.next() ? rs.getInt(1) : 0;
  267. if (clanId != 0)
  268. {
  269. L2Clan clan = ClanTable.getInstance().getClan(clanId);
  270. if (clan == null)
  271. {
  272. answer = 0; // jeezes!
  273. }
  274. else if (clan.getLeaderId() == objid)
  275. {
  276. answer = 2;
  277. }
  278. else
  279. {
  280. answer = 1;
  281. }
  282. }
  283. // Setting delete time
  284. if (answer == 0)
  285. {
  286. if (Config.DELETE_DAYS == 0)
  287. {
  288. deleteCharByObjId(objid);
  289. }
  290. else
  291. {
  292. try (PreparedStatement ps2 = con.prepareStatement("UPDATE characters SET deletetime=? WHERE charId=?"))
  293. {
  294. ps2.setLong(1, System.currentTimeMillis() + (Config.DELETE_DAYS * 86400000L)); // 24*60*60*1000 = 86400000
  295. ps2.setInt(2, objid);
  296. ps2.execute();
  297. }
  298. }
  299. LogRecord record = new LogRecord(Level.WARNING, "Delete");
  300. record.setParameters(new Object[]
  301. {
  302. objid,
  303. this
  304. });
  305. _logAccounting.log(record);
  306. }
  307. }
  308. return answer;
  309. }
  310. catch (Exception e)
  311. {
  312. _log.log(Level.SEVERE, "Error updating delete time of character.", e);
  313. return -1;
  314. }
  315. }
  316. /**
  317. * Save the L2PcInstance to the database.
  318. */
  319. public void saveCharToDisk()
  320. {
  321. try
  322. {
  323. if (getActiveChar() != null)
  324. {
  325. getActiveChar().storeMe();
  326. getActiveChar().storeRecommendations();
  327. if (Config.UPDATE_ITEMS_ON_CHAR_STORE)
  328. {
  329. getActiveChar().getInventory().updateDatabase();
  330. getActiveChar().getWarehouse().updateDatabase();
  331. }
  332. }
  333. }
  334. catch (Exception e)
  335. {
  336. _log.log(Level.SEVERE, "Error saving character..", e);
  337. }
  338. }
  339. public void markRestoredChar(int charslot)
  340. {
  341. final int objid = getObjectIdForSlot(charslot);
  342. if (objid < 0)
  343. {
  344. return;
  345. }
  346. try (Connection con = L2DatabaseFactory.getInstance().getConnection();
  347. PreparedStatement statement = con.prepareStatement("UPDATE characters SET deletetime=0 WHERE charId=?"))
  348. {
  349. statement.setInt(1, objid);
  350. statement.execute();
  351. }
  352. catch (Exception e)
  353. {
  354. _log.log(Level.SEVERE, "Error restoring character.", e);
  355. }
  356. final LogRecord record = new LogRecord(Level.WARNING, "Restore");
  357. record.setParameters(new Object[]
  358. {
  359. objid,
  360. this
  361. });
  362. _logAccounting.log(record);
  363. }
  364. public static void deleteCharByObjId(int objid)
  365. {
  366. if (objid < 0)
  367. {
  368. return;
  369. }
  370. CharNameTable.getInstance().removeName(objid);
  371. try (Connection con = L2DatabaseFactory.getInstance().getConnection())
  372. {
  373. try (PreparedStatement ps = con.prepareStatement("DELETE FROM character_contacts WHERE charId=? OR contactId=?"))
  374. {
  375. ps.setInt(1, objid);
  376. ps.setInt(2, objid);
  377. ps.execute();
  378. }
  379. try (PreparedStatement ps = con.prepareStatement("DELETE FROM character_friends WHERE charId=? OR friendId=?"))
  380. {
  381. ps.setInt(1, objid);
  382. ps.setInt(2, objid);
  383. ps.execute();
  384. }
  385. try (PreparedStatement ps = con.prepareStatement("DELETE FROM character_hennas WHERE charId=?"))
  386. {
  387. ps.setInt(1, objid);
  388. ps.execute();
  389. }
  390. try (PreparedStatement ps = con.prepareStatement("DELETE FROM character_macroses WHERE charId=?"))
  391. {
  392. ps.setInt(1, objid);
  393. ps.execute();
  394. }
  395. try (PreparedStatement ps = con.prepareStatement("DELETE FROM character_quests WHERE charId=?"))
  396. {
  397. ps.setInt(1, objid);
  398. ps.execute();
  399. }
  400. try (PreparedStatement ps = con.prepareStatement("DELETE FROM character_quest_global_data WHERE charId=?"))
  401. {
  402. ps.setInt(1, objid);
  403. ps.executeUpdate();
  404. }
  405. try (PreparedStatement ps = con.prepareStatement("DELETE FROM character_recipebook WHERE charId=?"))
  406. {
  407. ps.setInt(1, objid);
  408. ps.execute();
  409. }
  410. try (PreparedStatement ps = con.prepareStatement("DELETE FROM character_shortcuts WHERE charId=?"))
  411. {
  412. ps.setInt(1, objid);
  413. ps.execute();
  414. }
  415. try (PreparedStatement ps = con.prepareStatement("DELETE FROM character_skills WHERE charId=?"))
  416. {
  417. ps.setInt(1, objid);
  418. ps.execute();
  419. }
  420. try (PreparedStatement ps = con.prepareStatement("DELETE FROM character_skills_save WHERE charId=?"))
  421. {
  422. ps.setInt(1, objid);
  423. ps.execute();
  424. }
  425. try (PreparedStatement ps = con.prepareStatement("DELETE FROM character_subclasses WHERE charId=?"))
  426. {
  427. ps.setInt(1, objid);
  428. ps.execute();
  429. }
  430. try (PreparedStatement ps = con.prepareStatement("DELETE FROM heroes WHERE charId=?"))
  431. {
  432. ps.setInt(1, objid);
  433. ps.execute();
  434. }
  435. try (PreparedStatement ps = con.prepareStatement("DELETE FROM olympiad_nobles WHERE charId=?"))
  436. {
  437. ps.setInt(1, objid);
  438. ps.execute();
  439. }
  440. try (PreparedStatement ps = con.prepareStatement("DELETE FROM seven_signs WHERE charId=?"))
  441. {
  442. ps.setInt(1, objid);
  443. ps.execute();
  444. }
  445. try (PreparedStatement ps = con.prepareStatement("DELETE FROM pets WHERE item_obj_id IN (SELECT object_id FROM items WHERE items.owner_id=?)"))
  446. {
  447. ps.setInt(1, objid);
  448. ps.execute();
  449. }
  450. try (PreparedStatement ps = con.prepareStatement("DELETE FROM item_attributes WHERE itemId IN (SELECT object_id FROM items WHERE items.owner_id=?)"))
  451. {
  452. ps.setInt(1, objid);
  453. ps.execute();
  454. }
  455. try (PreparedStatement ps = con.prepareStatement("DELETE FROM items WHERE owner_id=?"))
  456. {
  457. ps.setInt(1, objid);
  458. ps.execute();
  459. }
  460. try (PreparedStatement ps = con.prepareStatement("DELETE FROM merchant_lease WHERE player_id=?"))
  461. {
  462. ps.setInt(1, objid);
  463. ps.execute();
  464. }
  465. try (PreparedStatement ps = con.prepareStatement("DELETE FROM character_raid_points WHERE charId=?"))
  466. {
  467. ps.setInt(1, objid);
  468. ps.execute();
  469. }
  470. try (PreparedStatement ps = con.prepareStatement("DELETE FROM character_reco_bonus WHERE charId=?"))
  471. {
  472. ps.setInt(1, objid);
  473. ps.execute();
  474. }
  475. try (PreparedStatement ps = con.prepareStatement("DELETE FROM character_instance_time WHERE charId=?"))
  476. {
  477. ps.setInt(1, objid);
  478. ps.execute();
  479. }
  480. try (PreparedStatement ps = con.prepareStatement("DELETE FROM character_variables WHERE charId=?"))
  481. {
  482. ps.setInt(1, objid);
  483. ps.execute();
  484. }
  485. try (PreparedStatement ps = con.prepareStatement("DELETE FROM characters WHERE charId=?"))
  486. {
  487. ps.setInt(1, objid);
  488. ps.execute();
  489. }
  490. if (Config.L2JMOD_ALLOW_WEDDING)
  491. {
  492. try (PreparedStatement ps = con.prepareStatement("DELETE FROM mods_wedding WHERE player1Id = ? OR player2Id = ?"))
  493. {
  494. ps.setInt(1, objid);
  495. ps.setInt(2, objid);
  496. ps.execute();
  497. }
  498. }
  499. }
  500. catch (Exception e)
  501. {
  502. _log.log(Level.SEVERE, "Error deleting character.", e);
  503. }
  504. }
  505. public L2PcInstance loadCharFromDisk(int charslot)
  506. {
  507. final int objId = getObjectIdForSlot(charslot);
  508. if (objId < 0)
  509. {
  510. return null;
  511. }
  512. L2PcInstance character = L2World.getInstance().getPlayer(objId);
  513. if (character != null)
  514. {
  515. // exploit prevention, should not happens in normal way
  516. _log.severe("Attempt of double login: " + character.getName() + "(" + objId + ") " + getAccountName());
  517. if (character.getClient() != null)
  518. {
  519. character.getClient().closeNow();
  520. }
  521. else
  522. {
  523. character.deleteMe();
  524. }
  525. return null;
  526. }
  527. character = L2PcInstance.load(objId);
  528. if (character != null)
  529. {
  530. // preinit some values for each login
  531. character.setRunning(); // running is default
  532. character.standUp(); // standing is default
  533. character.refreshOverloaded();
  534. character.refreshExpertisePenalty();
  535. character.setOnlineStatus(true, false);
  536. }
  537. else
  538. {
  539. _log.severe("could not restore in slot: " + charslot);
  540. }
  541. // setCharacter(character);
  542. return character;
  543. }
  544. /**
  545. * @param chars
  546. */
  547. public void setCharSelection(CharSelectInfoPackage[] chars)
  548. {
  549. _charSlotMapping = chars;
  550. }
  551. public CharSelectInfoPackage getCharSelection(int charslot)
  552. {
  553. if ((_charSlotMapping == null) || (charslot < 0) || (charslot >= _charSlotMapping.length))
  554. {
  555. return null;
  556. }
  557. return _charSlotMapping[charslot];
  558. }
  559. public SecondaryPasswordAuth getSecondaryAuth()
  560. {
  561. return _secondaryAuth;
  562. }
  563. public void close(L2GameServerPacket gsp)
  564. {
  565. if (getConnection() == null)
  566. {
  567. return; // ofline shop
  568. }
  569. if (_aditionalClosePacket != null)
  570. {
  571. getConnection().close(new L2GameServerPacket[]
  572. {
  573. _aditionalClosePacket,
  574. gsp
  575. });
  576. }
  577. else
  578. {
  579. getConnection().close(gsp);
  580. }
  581. }
  582. public void close(L2GameServerPacket[] gspArray)
  583. {
  584. if (getConnection() == null)
  585. {
  586. return; // ofline shop
  587. }
  588. getConnection().close(gspArray);
  589. }
  590. /**
  591. * @param charslot
  592. * @return
  593. */
  594. private int getObjectIdForSlot(int charslot)
  595. {
  596. final CharSelectInfoPackage info = getCharSelection(charslot);
  597. if (info == null)
  598. {
  599. _log.warning(toString() + " tried to delete Character in slot " + charslot + " but no characters exits at that slot.");
  600. return -1;
  601. }
  602. return info.getObjectId();
  603. }
  604. @Override
  605. protected void onForcedDisconnection()
  606. {
  607. LogRecord record = new LogRecord(Level.WARNING, "Disconnected abnormally");
  608. record.setParameters(new Object[]
  609. {
  610. this
  611. });
  612. _logAccounting.log(record);
  613. }
  614. @Override
  615. protected void onDisconnection()
  616. {
  617. // no long running tasks here, do it async
  618. try
  619. {
  620. ThreadPoolManager.getInstance().executeGeneral(new DisconnectTask());
  621. }
  622. catch (RejectedExecutionException e)
  623. {
  624. // server is closing
  625. }
  626. }
  627. /**
  628. * Close client connection with {@link ServerClose} packet
  629. */
  630. public void closeNow()
  631. {
  632. _isDetached = true; // prevents more packets execution
  633. close(ServerClose.STATIC_PACKET);
  634. synchronized (this)
  635. {
  636. if (_cleanupTask != null)
  637. {
  638. cancelCleanup();
  639. }
  640. _cleanupTask = ThreadPoolManager.getInstance().scheduleGeneral(new CleanupTask(), 0); // instant
  641. }
  642. }
  643. /**
  644. * Produces the best possible string representation of this client.
  645. */
  646. @Override
  647. public String toString()
  648. {
  649. try
  650. {
  651. final InetAddress address = getConnection().getInetAddress();
  652. switch (getState())
  653. {
  654. case CONNECTED:
  655. return "[IP: " + (address == null ? "disconnected" : address.getHostAddress()) + "]";
  656. case AUTHED:
  657. return "[Account: " + getAccountName() + " - IP: " + (address == null ? "disconnected" : address.getHostAddress()) + "]";
  658. case IN_GAME:
  659. return "[Character: " + (getActiveChar() == null ? "disconnected" : getActiveChar().getName() + "[" + getActiveChar().getObjectId() + "]") + " - Account: " + getAccountName() + " - IP: " + (address == null ? "disconnected" : address.getHostAddress()) + "]";
  660. default:
  661. throw new IllegalStateException("Missing state on switch");
  662. }
  663. }
  664. catch (NullPointerException e)
  665. {
  666. return "[Character read failed due to disconnect]";
  667. }
  668. }
  669. protected class DisconnectTask implements Runnable
  670. {
  671. @Override
  672. public void run()
  673. {
  674. boolean fast = true;
  675. try
  676. {
  677. if ((getActiveChar() != null) && !isDetached())
  678. {
  679. setDetached(true);
  680. if (offlineMode(getActiveChar()))
  681. {
  682. getActiveChar().leaveParty();
  683. OlympiadManager.getInstance().unRegisterNoble(getActiveChar());
  684. // If the L2PcInstance has Pet, unsummon it
  685. if (getActiveChar().hasSummon())
  686. {
  687. getActiveChar().getSummon().setRestoreSummon(true);
  688. getActiveChar().getSummon().unSummon(getActiveChar());
  689. // Dead pet wasn't unsummoned, broadcast npcinfo changes (pet will be without owner name - means owner offline)
  690. if (getActiveChar().getSummon() != null)
  691. {
  692. getActiveChar().getSummon().broadcastNpcInfo(0);
  693. }
  694. }
  695. if (Config.OFFLINE_SET_NAME_COLOR)
  696. {
  697. getActiveChar().getAppearance().setNameColor(Config.OFFLINE_NAME_COLOR);
  698. getActiveChar().broadcastUserInfo();
  699. }
  700. if (getActiveChar().getOfflineStartTime() == 0)
  701. {
  702. getActiveChar().setOfflineStartTime(System.currentTimeMillis());
  703. }
  704. final LogRecord record = new LogRecord(Level.INFO, "Entering offline mode");
  705. record.setParameters(new Object[]
  706. {
  707. L2GameClient.this
  708. });
  709. _logAccounting.log(record);
  710. return;
  711. }
  712. fast = !getActiveChar().isInCombat() && !getActiveChar().isLocked();
  713. }
  714. cleanMe(fast);
  715. }
  716. catch (Exception e1)
  717. {
  718. _log.log(Level.WARNING, "Error while disconnecting client.", e1);
  719. }
  720. }
  721. }
  722. /**
  723. * @param player the player to be check.
  724. * @return {@code true} if the player is allowed to remain as off-line shop.
  725. */
  726. protected boolean offlineMode(L2PcInstance player)
  727. {
  728. if (player.isInOlympiadMode() || player.isFestivalParticipant() || player.isBlockedFromExit() || player.isJailed() || (player.getVehicle() != null))
  729. {
  730. return false;
  731. }
  732. boolean canSetShop = false;
  733. switch (player.getPrivateStoreType())
  734. {
  735. case SELL:
  736. case PACKAGE_SELL:
  737. case BUY:
  738. {
  739. canSetShop = Config.OFFLINE_TRADE_ENABLE;
  740. break;
  741. }
  742. case MANUFACTURE:
  743. {
  744. canSetShop = Config.OFFLINE_TRADE_ENABLE;
  745. break;
  746. }
  747. default:
  748. {
  749. canSetShop = Config.OFFLINE_CRAFT_ENABLE && player.isInCraftMode();
  750. break;
  751. }
  752. }
  753. if (Config.OFFLINE_MODE_IN_PEACE_ZONE && !player.isInsideZone(ZoneId.PEACE))
  754. {
  755. canSetShop = false;
  756. }
  757. return canSetShop;
  758. }
  759. public void cleanMe(boolean fast)
  760. {
  761. try
  762. {
  763. synchronized (this)
  764. {
  765. if (_cleanupTask == null)
  766. {
  767. _cleanupTask = ThreadPoolManager.getInstance().scheduleGeneral(new CleanupTask(), fast ? 5 : 15000L);
  768. }
  769. }
  770. }
  771. catch (Exception e1)
  772. {
  773. _log.log(Level.WARNING, "Error during cleanup.", e1);
  774. }
  775. }
  776. protected class CleanupTask implements Runnable
  777. {
  778. @Override
  779. public void run()
  780. {
  781. try
  782. {
  783. // we are going to manually save the char bellow thus we can force the cancel
  784. if (_autoSaveInDB != null)
  785. {
  786. _autoSaveInDB.cancel(true);
  787. // ThreadPoolManager.getInstance().removeGeneral((Runnable) _autoSaveInDB);
  788. }
  789. if (getActiveChar() != null) // this should only happen on connection loss
  790. {
  791. if (getActiveChar().isLocked())
  792. {
  793. _log.log(Level.WARNING, "Player " + getActiveChar().getName() + " still performing subclass actions during disconnect.");
  794. }
  795. // we store all data from players who are disconnected while in an event in order to restore it in the next login
  796. if (L2Event.isParticipant(getActiveChar()))
  797. {
  798. L2Event.savePlayerEventStatus(getActiveChar());
  799. }
  800. // prevent closing again
  801. getActiveChar().setClient(null);
  802. if (getActiveChar().isOnline())
  803. {
  804. getActiveChar().deleteMe();
  805. AntiFeedManager.getInstance().onDisconnect(L2GameClient.this);
  806. }
  807. }
  808. setActiveChar(null);
  809. }
  810. catch (Exception e1)
  811. {
  812. _log.log(Level.WARNING, "Error while cleanup client.", e1);
  813. }
  814. finally
  815. {
  816. LoginServerThread.getInstance().sendLogout(getAccountName());
  817. }
  818. }
  819. }
  820. protected class AutoSaveTask implements Runnable
  821. {
  822. @Override
  823. public void run()
  824. {
  825. try
  826. {
  827. L2PcInstance player = getActiveChar();
  828. if ((player != null) && player.isOnline()) // safety precaution
  829. {
  830. saveCharToDisk();
  831. if (player.hasSummon())
  832. {
  833. player.getSummon().storeMe();
  834. }
  835. }
  836. }
  837. catch (Exception e)
  838. {
  839. _log.log(Level.SEVERE, "Error on AutoSaveTask.", e);
  840. }
  841. }
  842. }
  843. public boolean isProtocolOk()
  844. {
  845. return _protocol;
  846. }
  847. public void setProtocolOk(boolean b)
  848. {
  849. _protocol = b;
  850. }
  851. public boolean handleCheat(String punishment)
  852. {
  853. if (_activeChar != null)
  854. {
  855. Util.handleIllegalPlayerAction(_activeChar, toString() + ": " + punishment, Config.DEFAULT_PUNISH);
  856. return true;
  857. }
  858. Logger _logAudit = Logger.getLogger("audit");
  859. _logAudit.log(Level.INFO, "AUDIT: Client " + toString() + " kicked for reason: " + punishment);
  860. closeNow();
  861. return false;
  862. }
  863. /**
  864. * True if detached, or flood detected, or queue overflow detected and queue still not empty.
  865. * @return false if client can receive packets.
  866. */
  867. public boolean dropPacket()
  868. {
  869. if (_isDetached)
  870. {
  871. return true;
  872. }
  873. // flood protection
  874. if (getStats().countPacket(_packetQueue.size()))
  875. {
  876. sendPacket(ActionFailed.STATIC_PACKET);
  877. return true;
  878. }
  879. return getStats().dropPacket();
  880. }
  881. /**
  882. * Counts buffer underflow exceptions.
  883. */
  884. public void onBufferUnderflow()
  885. {
  886. if (getStats().countUnderflowException())
  887. {
  888. _log.severe("Client " + toString() + " - Disconnected: Too many buffer underflow exceptions.");
  889. closeNow();
  890. return;
  891. }
  892. if (_state == GameClientState.CONNECTED) // in CONNECTED state kick client immediately
  893. {
  894. if (Config.PACKET_HANDLER_DEBUG)
  895. {
  896. _log.severe("Client " + toString() + " - Disconnected, too many buffer underflows in non-authed state.");
  897. }
  898. closeNow();
  899. }
  900. }
  901. /**
  902. * Counts unknown packets
  903. */
  904. public void onUnknownPacket()
  905. {
  906. if (getStats().countUnknownPacket())
  907. {
  908. _log.severe("Client " + toString() + " - Disconnected: Too many unknown packets.");
  909. closeNow();
  910. return;
  911. }
  912. if (_state == GameClientState.CONNECTED) // in CONNECTED state kick client immediately
  913. {
  914. if (Config.PACKET_HANDLER_DEBUG)
  915. {
  916. _log.severe("Client " + toString() + " - Disconnected, too many unknown packets in non-authed state.");
  917. }
  918. closeNow();
  919. }
  920. }
  921. /**
  922. * Add packet to the queue and start worker thread if needed
  923. * @param packet
  924. */
  925. public void execute(ReceivablePacket<L2GameClient> packet)
  926. {
  927. if (getStats().countFloods())
  928. {
  929. _log.severe("Client " + toString() + " - Disconnected, too many floods:" + getStats().longFloods + " long and " + getStats().shortFloods + " short.");
  930. closeNow();
  931. return;
  932. }
  933. if (!_packetQueue.offer(packet))
  934. {
  935. if (getStats().countQueueOverflow())
  936. {
  937. _log.severe("Client " + toString() + " - Disconnected, too many queue overflows.");
  938. closeNow();
  939. }
  940. else
  941. {
  942. sendPacket(ActionFailed.STATIC_PACKET);
  943. }
  944. return;
  945. }
  946. if (_queueLock.isLocked())
  947. {
  948. return;
  949. }
  950. try
  951. {
  952. if (_state == GameClientState.CONNECTED)
  953. {
  954. if (getStats().processedPackets > 3)
  955. {
  956. if (Config.PACKET_HANDLER_DEBUG)
  957. {
  958. _log.severe("Client " + toString() + " - Disconnected, too many packets in non-authed state.");
  959. }
  960. closeNow();
  961. return;
  962. }
  963. ThreadPoolManager.getInstance().executeIOPacket(this);
  964. }
  965. else
  966. {
  967. ThreadPoolManager.getInstance().executePacket(this);
  968. }
  969. }
  970. catch (RejectedExecutionException e)
  971. {
  972. // if the server is shutdown we ignore
  973. if (!ThreadPoolManager.getInstance().isShutdown())
  974. {
  975. _log.severe("Failed executing: " + packet.getClass().getSimpleName() + " for Client: " + toString());
  976. }
  977. }
  978. }
  979. @Override
  980. public void run()
  981. {
  982. if (!_queueLock.tryLock())
  983. {
  984. return;
  985. }
  986. try
  987. {
  988. int count = 0;
  989. ReceivablePacket<L2GameClient> packet;
  990. while (true)
  991. {
  992. packet = _packetQueue.poll();
  993. if (packet == null)
  994. {
  995. return;
  996. }
  997. if (_isDetached) // clear queue immediately after detach
  998. {
  999. _packetQueue.clear();
  1000. return;
  1001. }
  1002. try
  1003. {
  1004. packet.run();
  1005. }
  1006. catch (Exception e)
  1007. {
  1008. _log.severe("Exception during execution " + packet.getClass().getSimpleName() + ", client: " + toString() + "," + e.getMessage());
  1009. }
  1010. count++;
  1011. if (getStats().countBurst(count))
  1012. {
  1013. return;
  1014. }
  1015. }
  1016. }
  1017. finally
  1018. {
  1019. _queueLock.unlock();
  1020. }
  1021. }
  1022. public void setClientTracert(int[][] tracert)
  1023. {
  1024. trace = tracert;
  1025. }
  1026. public int[][] getTrace()
  1027. {
  1028. return trace;
  1029. }
  1030. private boolean cancelCleanup()
  1031. {
  1032. final Future<?> task = _cleanupTask;
  1033. if (task != null)
  1034. {
  1035. _cleanupTask = null;
  1036. return task.cancel(true);
  1037. }
  1038. return false;
  1039. }
  1040. public void setAditionalClosePacket(L2GameServerPacket aditionalClosePacket)
  1041. {
  1042. _aditionalClosePacket = aditionalClosePacket;
  1043. }
  1044. }