Shutdown.java 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665
  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;
  20. import java.util.logging.Level;
  21. import java.util.logging.Logger;
  22. import com.l2jserver.Config;
  23. import com.l2jserver.L2DatabaseFactory;
  24. import com.l2jserver.UPnPService;
  25. import com.l2jserver.gameserver.datatables.BotReportTable;
  26. import com.l2jserver.gameserver.datatables.ClanTable;
  27. import com.l2jserver.gameserver.datatables.OfflineTradersTable;
  28. import com.l2jserver.gameserver.instancemanager.CHSiegeManager;
  29. import com.l2jserver.gameserver.instancemanager.CastleManorManager;
  30. import com.l2jserver.gameserver.instancemanager.CursedWeaponsManager;
  31. import com.l2jserver.gameserver.instancemanager.GlobalVariablesManager;
  32. import com.l2jserver.gameserver.instancemanager.GrandBossManager;
  33. import com.l2jserver.gameserver.instancemanager.ItemAuctionManager;
  34. import com.l2jserver.gameserver.instancemanager.ItemsOnGroundManager;
  35. import com.l2jserver.gameserver.instancemanager.QuestManager;
  36. import com.l2jserver.gameserver.instancemanager.RaidBossSpawnManager;
  37. import com.l2jserver.gameserver.model.L2World;
  38. import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
  39. import com.l2jserver.gameserver.model.entity.Hero;
  40. import com.l2jserver.gameserver.model.olympiad.Olympiad;
  41. import com.l2jserver.gameserver.network.L2GameClient;
  42. import com.l2jserver.gameserver.network.SystemMessageId;
  43. import com.l2jserver.gameserver.network.communityserver.CommunityServerThread;
  44. import com.l2jserver.gameserver.network.gameserverpackets.ServerStatus;
  45. import com.l2jserver.gameserver.network.serverpackets.ServerClose;
  46. import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
  47. import com.l2jserver.gameserver.util.Broadcast;
  48. /**
  49. * This class provides the functions for shutting down and restarting the server.<br>
  50. * It closes all open client connections and saves all data.
  51. * @version $Revision: 1.2.4.5 $ $Date: 2005/03/27 15:29:09 $
  52. */
  53. public class Shutdown extends Thread
  54. {
  55. private static final Logger _log = Logger.getLogger(Shutdown.class.getName());
  56. private static Shutdown _counterInstance = null;
  57. private int _secondsShut;
  58. private int _shutdownMode;
  59. public static final int SIGTERM = 0;
  60. public static final int GM_SHUTDOWN = 1;
  61. public static final int GM_RESTART = 2;
  62. public static final int ABORT = 3;
  63. private static final String[] MODE_TEXT =
  64. {
  65. "SIGTERM",
  66. "shutting down",
  67. "restarting",
  68. "aborting"
  69. };
  70. /**
  71. * This function starts a shutdown count down from Telnet (Copied from Function startShutdown())
  72. * @param seconds seconds until shutdown
  73. */
  74. private void SendServerQuit(int seconds)
  75. {
  76. SystemMessage sysm = SystemMessage.getSystemMessage(SystemMessageId.THE_SERVER_WILL_BE_COMING_DOWN_IN_S1_SECONDS);
  77. sysm.addInt(seconds);
  78. Broadcast.toAllOnlinePlayers(sysm);
  79. }
  80. public void startTelnetShutdown(String IP, int seconds, boolean restart)
  81. {
  82. _log.warning("IP: " + IP + " issued shutdown command. " + MODE_TEXT[_shutdownMode] + " in " + seconds + " seconds!");
  83. if (restart)
  84. {
  85. _shutdownMode = GM_RESTART;
  86. }
  87. else
  88. {
  89. _shutdownMode = GM_SHUTDOWN;
  90. }
  91. if (_shutdownMode > 0)
  92. {
  93. switch (seconds)
  94. {
  95. case 540:
  96. case 480:
  97. case 420:
  98. case 360:
  99. case 300:
  100. case 240:
  101. case 180:
  102. case 120:
  103. case 60:
  104. case 30:
  105. case 10:
  106. case 5:
  107. case 4:
  108. case 3:
  109. case 2:
  110. case 1:
  111. break;
  112. default:
  113. SendServerQuit(seconds);
  114. }
  115. }
  116. if (_counterInstance != null)
  117. {
  118. _counterInstance._abort();
  119. }
  120. _counterInstance = new Shutdown(seconds, restart);
  121. _counterInstance.start();
  122. }
  123. /**
  124. * This function aborts a running countdown
  125. * @param IP IP Which Issued shutdown command
  126. */
  127. public void telnetAbort(String IP)
  128. {
  129. _log.warning("IP: " + IP + " issued shutdown ABORT. " + MODE_TEXT[_shutdownMode] + " has been stopped!");
  130. if (_counterInstance != null)
  131. {
  132. _counterInstance._abort();
  133. Announcements _an = Announcements.getInstance();
  134. _an.announceToAll("Server aborts " + MODE_TEXT[_shutdownMode] + " and continues normal operation!");
  135. }
  136. }
  137. /**
  138. * Default constructor is only used internal to create the shutdown-hook instance
  139. */
  140. protected Shutdown()
  141. {
  142. _secondsShut = -1;
  143. _shutdownMode = SIGTERM;
  144. }
  145. /**
  146. * This creates a countdown instance of Shutdown.
  147. * @param seconds how many seconds until shutdown
  148. * @param restart true is the server shall restart after shutdown
  149. */
  150. public Shutdown(int seconds, boolean restart)
  151. {
  152. if (seconds < 0)
  153. {
  154. seconds = 0;
  155. }
  156. _secondsShut = seconds;
  157. if (restart)
  158. {
  159. _shutdownMode = GM_RESTART;
  160. }
  161. else
  162. {
  163. _shutdownMode = GM_SHUTDOWN;
  164. }
  165. }
  166. /**
  167. * This function is called, when a new thread starts if this thread is the thread of getInstance, then this is the shutdown hook and we save all data and disconnect all clients.<br>
  168. * After this thread ends, the server will completely exit if this is not the thread of getInstance, then this is a countdown thread.<br>
  169. * We start the countdown, and when we finished it, and it was not aborted, we tell the shutdown-hook why we call exit, and then call exit when the exit status of the server is 1, startServer.sh / startServer.bat will restart the server.
  170. */
  171. @Override
  172. public void run()
  173. {
  174. if (this == getInstance())
  175. {
  176. TimeCounter tc = new TimeCounter();
  177. TimeCounter tc1 = new TimeCounter();
  178. try
  179. {
  180. UPnPService.getInstance().removeAllPorts();
  181. _log.info("UPnP Service: All ports mappings deleted (" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
  182. }
  183. catch (Throwable t)
  184. {
  185. _log.log(Level.WARNING, "Error while removing UPnP port mappings: ", t);
  186. }
  187. try
  188. {
  189. if ((Config.OFFLINE_TRADE_ENABLE || Config.OFFLINE_CRAFT_ENABLE) && Config.RESTORE_OFFLINERS)
  190. {
  191. OfflineTradersTable.getInstance().storeOffliners();
  192. _log.info("Offline Traders Table: Offline shops stored(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
  193. }
  194. }
  195. catch (Throwable t)
  196. {
  197. _log.log(Level.WARNING, "Error saving offline shops.", t);
  198. }
  199. try
  200. {
  201. disconnectAllCharacters();
  202. _log.info("All players disconnected and saved(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
  203. }
  204. catch (Throwable t)
  205. {
  206. // ignore
  207. }
  208. // ensure all services are stopped
  209. try
  210. {
  211. GameTimeController.getInstance().stopTimer();
  212. _log.info("Game Time Controller: Timer stopped(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
  213. }
  214. catch (Throwable t)
  215. {
  216. // ignore
  217. }
  218. // stop all threadpolls
  219. try
  220. {
  221. ThreadPoolManager.getInstance().shutdown();
  222. _log.info("Thread Pool Manager: Manager has been shut down(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
  223. }
  224. catch (Throwable t)
  225. {
  226. // ignore
  227. }
  228. try
  229. {
  230. CommunityServerThread.getInstance().interrupt();
  231. _log.info("Community Server Thread: Thread interruped(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
  232. }
  233. catch (Throwable t)
  234. {
  235. // ignore
  236. }
  237. try
  238. {
  239. LoginServerThread.getInstance().interrupt();
  240. _log.info("Login Server Thread: Thread interruped(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
  241. }
  242. catch (Throwable t)
  243. {
  244. // ignore
  245. }
  246. // last byebye, save all data and quit this server
  247. saveData();
  248. tc.restartCounter();
  249. // saveData sends messages to exit players, so shutdown selector after it
  250. try
  251. {
  252. GameServer.gameServer.getSelectorThread().shutdown();
  253. _log.info("Game Server: Selector thread has been shut down(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
  254. }
  255. catch (Throwable t)
  256. {
  257. // ignore
  258. }
  259. // commit data, last chance
  260. try
  261. {
  262. L2DatabaseFactory.getInstance().shutdown();
  263. _log.info("L2Database Factory: Database connection has been shut down(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
  264. }
  265. catch (Throwable t)
  266. {
  267. }
  268. // server will quit, when this function ends.
  269. if (getInstance()._shutdownMode == GM_RESTART)
  270. {
  271. Runtime.getRuntime().halt(2);
  272. }
  273. else
  274. {
  275. Runtime.getRuntime().halt(0);
  276. }
  277. _log.info("The server has been successfully shut down in " + (tc1.getEstimatedTime() / 1000) + "seconds.");
  278. }
  279. else
  280. {
  281. // gm shutdown: send warnings and then call exit to start shutdown sequence
  282. countdown();
  283. // last point where logging is operational :(
  284. _log.warning("GM shutdown countdown is over. " + MODE_TEXT[_shutdownMode] + " NOW!");
  285. switch (_shutdownMode)
  286. {
  287. case GM_SHUTDOWN:
  288. getInstance().setMode(GM_SHUTDOWN);
  289. System.exit(0);
  290. break;
  291. case GM_RESTART:
  292. getInstance().setMode(GM_RESTART);
  293. System.exit(2);
  294. break;
  295. case ABORT:
  296. LoginServerThread.getInstance().setServerStatus(ServerStatus.STATUS_AUTO);
  297. break;
  298. }
  299. }
  300. }
  301. /**
  302. * This functions starts a shutdown countdown.
  303. * @param activeChar GM who issued the shutdown command
  304. * @param seconds seconds until shutdown
  305. * @param restart true if the server will restart after shutdown
  306. */
  307. public void startShutdown(L2PcInstance activeChar, int seconds, boolean restart)
  308. {
  309. if (restart)
  310. {
  311. _shutdownMode = GM_RESTART;
  312. }
  313. else
  314. {
  315. _shutdownMode = GM_SHUTDOWN;
  316. }
  317. _log.warning("GM: " + activeChar.getName() + "(" + activeChar.getObjectId() + ") issued shutdown command. " + MODE_TEXT[_shutdownMode] + " in " + seconds + " seconds!");
  318. if (_shutdownMode > 0)
  319. {
  320. switch (seconds)
  321. {
  322. case 540:
  323. case 480:
  324. case 420:
  325. case 360:
  326. case 300:
  327. case 240:
  328. case 180:
  329. case 120:
  330. case 60:
  331. case 30:
  332. case 10:
  333. case 5:
  334. case 4:
  335. case 3:
  336. case 2:
  337. case 1:
  338. break;
  339. default:
  340. SendServerQuit(seconds);
  341. }
  342. }
  343. if (_counterInstance != null)
  344. {
  345. _counterInstance._abort();
  346. }
  347. // the main instance should only run for shutdown hook, so we start a new instance
  348. _counterInstance = new Shutdown(seconds, restart);
  349. _counterInstance.start();
  350. }
  351. /**
  352. * This function aborts a running countdown.
  353. * @param activeChar GM who issued the abort command
  354. */
  355. public void abort(L2PcInstance activeChar)
  356. {
  357. _log.warning("GM: " + activeChar.getName() + "(" + activeChar.getObjectId() + ") issued shutdown ABORT. " + MODE_TEXT[_shutdownMode] + " has been stopped!");
  358. if (_counterInstance != null)
  359. {
  360. _counterInstance._abort();
  361. Announcements _an = Announcements.getInstance();
  362. _an.announceToAll("Server aborts " + MODE_TEXT[_shutdownMode] + " and continues normal operation!");
  363. }
  364. }
  365. /**
  366. * Set the shutdown mode.
  367. * @param mode what mode shall be set
  368. */
  369. private void setMode(int mode)
  370. {
  371. _shutdownMode = mode;
  372. }
  373. /**
  374. * Set shutdown mode to ABORT.
  375. */
  376. private void _abort()
  377. {
  378. _shutdownMode = ABORT;
  379. }
  380. /**
  381. * This counts the countdown and reports it to all players countdown is aborted if mode changes to ABORT.
  382. */
  383. private void countdown()
  384. {
  385. try
  386. {
  387. while (_secondsShut > 0)
  388. {
  389. switch (_secondsShut)
  390. {
  391. case 540:
  392. SendServerQuit(540);
  393. break;
  394. case 480:
  395. SendServerQuit(480);
  396. break;
  397. case 420:
  398. SendServerQuit(420);
  399. break;
  400. case 360:
  401. SendServerQuit(360);
  402. break;
  403. case 300:
  404. SendServerQuit(300);
  405. break;
  406. case 240:
  407. SendServerQuit(240);
  408. break;
  409. case 180:
  410. SendServerQuit(180);
  411. break;
  412. case 120:
  413. SendServerQuit(120);
  414. break;
  415. case 60:
  416. LoginServerThread.getInstance().setServerStatus(ServerStatus.STATUS_DOWN); // avoids new players from logging in
  417. SendServerQuit(60);
  418. break;
  419. case 30:
  420. SendServerQuit(30);
  421. break;
  422. case 10:
  423. SendServerQuit(10);
  424. break;
  425. case 5:
  426. SendServerQuit(5);
  427. break;
  428. case 4:
  429. SendServerQuit(4);
  430. break;
  431. case 3:
  432. SendServerQuit(3);
  433. break;
  434. case 2:
  435. SendServerQuit(2);
  436. break;
  437. case 1:
  438. SendServerQuit(1);
  439. break;
  440. }
  441. _secondsShut--;
  442. int delay = 1000; // milliseconds
  443. Thread.sleep(delay);
  444. if (_shutdownMode == ABORT)
  445. {
  446. break;
  447. }
  448. }
  449. }
  450. catch (InterruptedException e)
  451. {
  452. // this will never happen
  453. }
  454. }
  455. /**
  456. * This sends a last byebye, disconnects all players and saves data.
  457. */
  458. private void saveData()
  459. {
  460. switch (_shutdownMode)
  461. {
  462. case SIGTERM:
  463. _log.info("SIGTERM received. Shutting down NOW!");
  464. break;
  465. case GM_SHUTDOWN:
  466. _log.info("GM shutdown received. Shutting down NOW!");
  467. break;
  468. case GM_RESTART:
  469. _log.info("GM restart received. Restarting NOW!");
  470. break;
  471. }
  472. /*
  473. * if (Config.ACTIVATE_POSITION_RECORDER) Universe.getInstance().implode(true);
  474. */
  475. TimeCounter tc = new TimeCounter();
  476. // Seven Signs data is now saved along with Festival data.
  477. if (!SevenSigns.getInstance().isSealValidationPeriod())
  478. {
  479. SevenSignsFestival.getInstance().saveFestivalData(false);
  480. _log.info("SevenSignsFestival: Festival data saved(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
  481. }
  482. // Save Seven Signs data before closing. :)
  483. SevenSigns.getInstance().saveSevenSignsData();
  484. _log.info("SevenSigns: Seven Signs data saved(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
  485. SevenSigns.getInstance().saveSevenSignsStatus();
  486. _log.info("SevenSigns: Seven Signs status saved(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
  487. // Save all raidboss and GrandBoss status ^_^
  488. RaidBossSpawnManager.getInstance().cleanUp();
  489. _log.info("RaidBossSpawnManager: All raidboss info saved(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
  490. GrandBossManager.getInstance().cleanUp();
  491. _log.info("GrandBossManager: All Grand Boss info saved(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
  492. ItemAuctionManager.getInstance().shutdown();
  493. _log.info("Item Auction Manager: All tasks stopped(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
  494. Olympiad.getInstance().saveOlympiadStatus();
  495. _log.info("Olympiad System: Data saved(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
  496. Hero.getInstance().shutdown();
  497. _log.info("Hero System: Data saved(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
  498. ClanTable.getInstance().storeClanScore();
  499. _log.info("Clan System: Data saved(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
  500. // Save Cursed Weapons data before closing.
  501. CursedWeaponsManager.getInstance().saveData();
  502. _log.info("Cursed Weapons Manager: Data saved(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
  503. // Save all manor data
  504. if (!Config.ALT_MANOR_SAVE_ALL_ACTIONS)
  505. {
  506. CastleManorManager.getInstance().storeMe();
  507. _log.info("Castle Manor Manager: Data saved(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
  508. }
  509. CHSiegeManager.getInstance().onServerShutDown();
  510. _log.info("CHSiegeManager: Siegable hall attacker lists saved!");
  511. // Save all global (non-player specific) Quest data that needs to persist after reboot
  512. QuestManager.getInstance().save();
  513. _log.info("Quest Manager: Data saved(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
  514. // Save all global variables data
  515. GlobalVariablesManager.getInstance().storeMe();
  516. _log.info("Global Variables Manager: Variables saved(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
  517. // Save items on ground before closing
  518. if (Config.SAVE_DROPPED_ITEM)
  519. {
  520. ItemsOnGroundManager.getInstance().saveInDb();
  521. _log.info("Items On Ground Manager: Data saved(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
  522. ItemsOnGroundManager.getInstance().cleanUp();
  523. _log.info("Items On Ground Manager: Cleaned up(" + tc.getEstimatedTimeAndRestartCounter() + "ms).");
  524. }
  525. // Save bot reports to database
  526. if (Config.BOTREPORT_ENABLE)
  527. {
  528. BotReportTable.getInstance().saveReportedCharData();
  529. _log.info("Bot Report Table: Sucessfully saved reports to database!");
  530. }
  531. try
  532. {
  533. Thread.sleep(5000);
  534. }
  535. catch (InterruptedException e)
  536. {
  537. // never happens :p
  538. }
  539. }
  540. /**
  541. * This disconnects all clients from the server.
  542. */
  543. private void disconnectAllCharacters()
  544. {
  545. for (L2PcInstance player : L2World.getInstance().getPlayers())
  546. {
  547. // Logout Character
  548. try
  549. {
  550. L2GameClient client = player.getClient();
  551. if ((client != null) && !client.isDetached())
  552. {
  553. client.close(ServerClose.STATIC_PACKET);
  554. client.setActiveChar(null);
  555. player.setClient(null);
  556. }
  557. player.deleteMe();
  558. }
  559. catch (Throwable t)
  560. {
  561. _log.log(Level.WARNING, "Failed logour char " + player, t);
  562. }
  563. }
  564. }
  565. /**
  566. * A simple class used to track down the estimated time of method executions.<br>
  567. * Once this class is created, it saves the start time, and when you want to get the estimated time, use the getEstimatedTime() method.
  568. */
  569. private static final class TimeCounter
  570. {
  571. private long _startTime;
  572. protected TimeCounter()
  573. {
  574. restartCounter();
  575. }
  576. protected void restartCounter()
  577. {
  578. _startTime = System.currentTimeMillis();
  579. }
  580. protected long getEstimatedTimeAndRestartCounter()
  581. {
  582. final long toReturn = System.currentTimeMillis() - _startTime;
  583. restartCounter();
  584. return toReturn;
  585. }
  586. protected long getEstimatedTime()
  587. {
  588. return System.currentTimeMillis() - _startTime;
  589. }
  590. }
  591. /**
  592. * Get the shutdown-hook instance the shutdown-hook instance is created by the first call of this function, but it has to be registered externally.<br>
  593. * @return instance of Shutdown, to be used as shutdown hook
  594. */
  595. public static Shutdown getInstance()
  596. {
  597. return SingletonHolder._instance;
  598. }
  599. private static class SingletonHolder
  600. {
  601. protected static final Shutdown _instance = new Shutdown();
  602. }
  603. }