RaidBossSpawnManager.java 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. /*
  2. * Copyright (C) 2004-2015 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.instancemanager;
  20. import java.sql.Connection;
  21. import java.sql.PreparedStatement;
  22. import java.sql.ResultSet;
  23. import java.sql.SQLException;
  24. import java.util.Calendar;
  25. import java.util.Map;
  26. import java.util.concurrent.ScheduledFuture;
  27. import java.util.logging.Level;
  28. import java.util.logging.Logger;
  29. import javolution.util.FastMap;
  30. import com.l2jserver.Config;
  31. import com.l2jserver.L2DatabaseFactory;
  32. import com.l2jserver.gameserver.ThreadPoolManager;
  33. import com.l2jserver.gameserver.datatables.SpawnTable;
  34. import com.l2jserver.gameserver.model.L2Spawn;
  35. import com.l2jserver.gameserver.model.StatsSet;
  36. import com.l2jserver.gameserver.model.actor.instance.L2RaidBossInstance;
  37. import com.l2jserver.util.Rnd;
  38. /**
  39. * Raid Boss spawn manager.
  40. * @author godson
  41. */
  42. public class RaidBossSpawnManager
  43. {
  44. private static final Logger _log = Logger.getLogger(RaidBossSpawnManager.class.getName());
  45. protected static final Map<Integer, L2RaidBossInstance> _bosses = new FastMap<>();
  46. protected static final Map<Integer, L2Spawn> _spawns = new FastMap<>();
  47. protected static final Map<Integer, StatsSet> _storedInfo = new FastMap<>();
  48. protected static final Map<Integer, ScheduledFuture<?>> _schedules = new FastMap<>();
  49. public static enum StatusEnum
  50. {
  51. ALIVE,
  52. DEAD,
  53. UNDEFINED
  54. }
  55. /**
  56. * Instantiates a new raid boss spawn manager.
  57. */
  58. protected RaidBossSpawnManager()
  59. {
  60. load();
  61. }
  62. /**
  63. * Load.
  64. */
  65. public void load()
  66. {
  67. _bosses.clear();
  68. _spawns.clear();
  69. _storedInfo.clear();
  70. _schedules.clear();
  71. try (Connection con = L2DatabaseFactory.getInstance().getConnection();
  72. PreparedStatement statement = con.prepareStatement("SELECT * FROM raidboss_spawnlist ORDER BY boss_id");
  73. ResultSet rset = statement.executeQuery())
  74. {
  75. while (rset.next())
  76. {
  77. final L2Spawn spawnDat = new L2Spawn(rset.getInt("boss_id"));
  78. spawnDat.setX(rset.getInt("loc_x"));
  79. spawnDat.setY(rset.getInt("loc_y"));
  80. spawnDat.setZ(rset.getInt("loc_z"));
  81. spawnDat.setAmount(rset.getInt("amount"));
  82. spawnDat.setHeading(rset.getInt("heading"));
  83. spawnDat.setRespawnDelay(rset.getInt("respawn_delay"), rset.getInt("respawn_random"));
  84. addNewSpawn(spawnDat, rset.getLong("respawn_time"), rset.getDouble("currentHP"), rset.getDouble("currentMP"), false);
  85. }
  86. _log.info(getClass().getSimpleName() + ": Loaded " + _bosses.size() + " Instances");
  87. _log.info(getClass().getSimpleName() + ": Scheduled " + _schedules.size() + " Instances");
  88. }
  89. catch (SQLException e)
  90. {
  91. _log.warning(getClass().getSimpleName() + ": Couldnt load raidboss_spawnlist table");
  92. }
  93. catch (Exception e)
  94. {
  95. _log.log(Level.WARNING, getClass().getSimpleName() + ": Error while initializing RaidBossSpawnManager: " + e.getMessage(), e);
  96. }
  97. }
  98. private static class SpawnSchedule implements Runnable
  99. {
  100. private static final Logger _log = Logger.getLogger(SpawnSchedule.class.getName());
  101. private final int bossId;
  102. /**
  103. * Instantiates a new spawn schedule.
  104. * @param npcId the npc id
  105. */
  106. public SpawnSchedule(int npcId)
  107. {
  108. bossId = npcId;
  109. }
  110. @Override
  111. public void run()
  112. {
  113. L2RaidBossInstance raidboss = null;
  114. if (bossId == 25328)
  115. {
  116. raidboss = DayNightSpawnManager.getInstance().handleBoss(_spawns.get(bossId));
  117. }
  118. else
  119. {
  120. raidboss = (L2RaidBossInstance) _spawns.get(bossId).doSpawn();
  121. }
  122. if (raidboss != null)
  123. {
  124. raidboss.setRaidStatus(StatusEnum.ALIVE);
  125. final StatsSet info = new StatsSet();
  126. info.set("currentHP", raidboss.getCurrentHp());
  127. info.set("currentMP", raidboss.getCurrentMp());
  128. info.set("respawnTime", 0L);
  129. _storedInfo.put(bossId, info);
  130. _log.info(getClass().getSimpleName() + ": Spawning Raid Boss " + raidboss.getName());
  131. _bosses.put(bossId, raidboss);
  132. }
  133. _schedules.remove(bossId);
  134. }
  135. }
  136. /**
  137. * Update status.
  138. * @param boss the boss
  139. * @param isBossDead the is boss dead
  140. */
  141. public void updateStatus(L2RaidBossInstance boss, boolean isBossDead)
  142. {
  143. final StatsSet info = _storedInfo.get(boss.getId());
  144. if (info == null)
  145. {
  146. return;
  147. }
  148. if (isBossDead)
  149. {
  150. boss.setRaidStatus(StatusEnum.DEAD);
  151. final int respawnMinDelay = (int) (boss.getSpawn().getRespawnMinDelay() * Config.RAID_MIN_RESPAWN_MULTIPLIER);
  152. final int respawnMaxDelay = (int) (boss.getSpawn().getRespawnMaxDelay() * Config.RAID_MAX_RESPAWN_MULTIPLIER);
  153. final int respawnDelay = Rnd.get(respawnMinDelay, respawnMaxDelay);
  154. final long respawnTime = Calendar.getInstance().getTimeInMillis() + respawnDelay;
  155. info.set("currentHP", boss.getMaxHp());
  156. info.set("currentMP", boss.getMaxMp());
  157. info.set("respawnTime", respawnTime);
  158. if (!_schedules.containsKey(boss.getId()) && ((respawnMinDelay > 0) || (respawnMaxDelay > 0)))
  159. {
  160. final Calendar time = Calendar.getInstance();
  161. time.setTimeInMillis(respawnTime);
  162. _log.info(getClass().getSimpleName() + ": Updated " + boss.getName() + " respawn time to " + time.getTime());
  163. _schedules.put(boss.getId(), ThreadPoolManager.getInstance().scheduleGeneral(new SpawnSchedule(boss.getId()), respawnDelay));
  164. updateDb();
  165. }
  166. }
  167. else
  168. {
  169. boss.setRaidStatus(StatusEnum.ALIVE);
  170. info.set("currentHP", boss.getCurrentHp());
  171. info.set("currentMP", boss.getCurrentMp());
  172. info.set("respawnTime", 0L);
  173. }
  174. _storedInfo.put(boss.getId(), info);
  175. }
  176. /**
  177. * Adds the new spawn.
  178. * @param spawnDat the spawn dat
  179. * @param respawnTime the respawn time
  180. * @param currentHP the current hp
  181. * @param currentMP the current mp
  182. * @param storeInDb the store in db
  183. */
  184. public void addNewSpawn(L2Spawn spawnDat, long respawnTime, double currentHP, double currentMP, boolean storeInDb)
  185. {
  186. if (spawnDat == null)
  187. {
  188. return;
  189. }
  190. if (_spawns.containsKey(spawnDat.getId()))
  191. {
  192. return;
  193. }
  194. final int bossId = spawnDat.getId();
  195. final long time = Calendar.getInstance().getTimeInMillis();
  196. SpawnTable.getInstance().addNewSpawn(spawnDat, false);
  197. if ((respawnTime == 0L) || (time > respawnTime))
  198. {
  199. L2RaidBossInstance raidboss = null;
  200. if (bossId == 25328)
  201. {
  202. raidboss = DayNightSpawnManager.getInstance().handleBoss(spawnDat);
  203. }
  204. else
  205. {
  206. raidboss = (L2RaidBossInstance) spawnDat.doSpawn();
  207. }
  208. if (raidboss != null)
  209. {
  210. raidboss.setCurrentHp(currentHP);
  211. raidboss.setCurrentMp(currentMP);
  212. raidboss.setRaidStatus(StatusEnum.ALIVE);
  213. _bosses.put(bossId, raidboss);
  214. final StatsSet info = new StatsSet();
  215. info.set("currentHP", currentHP);
  216. info.set("currentMP", currentMP);
  217. info.set("respawnTime", 0L);
  218. _storedInfo.put(bossId, info);
  219. }
  220. }
  221. else
  222. {
  223. final long spawnTime = respawnTime - Calendar.getInstance().getTimeInMillis();
  224. _schedules.put(bossId, ThreadPoolManager.getInstance().scheduleGeneral(new SpawnSchedule(bossId), spawnTime));
  225. }
  226. _spawns.put(bossId, spawnDat);
  227. if (storeInDb)
  228. {
  229. try (Connection con = L2DatabaseFactory.getInstance().getConnection();
  230. PreparedStatement statement = con.prepareStatement("INSERT INTO raidboss_spawnlist (boss_id,amount,loc_x,loc_y,loc_z,heading,respawn_time,currentHp,currentMp) VALUES(?,?,?,?,?,?,?,?,?)"))
  231. {
  232. statement.setInt(1, spawnDat.getId());
  233. statement.setInt(2, spawnDat.getAmount());
  234. statement.setInt(3, spawnDat.getX());
  235. statement.setInt(4, spawnDat.getY());
  236. statement.setInt(5, spawnDat.getZ());
  237. statement.setInt(6, spawnDat.getHeading());
  238. statement.setLong(7, respawnTime);
  239. statement.setDouble(8, currentHP);
  240. statement.setDouble(9, currentMP);
  241. statement.execute();
  242. }
  243. catch (Exception e)
  244. {
  245. // problem with storing spawn
  246. _log.log(Level.WARNING, getClass().getSimpleName() + ": Could not store raidboss #" + bossId + " in the DB:" + e.getMessage(), e);
  247. }
  248. }
  249. }
  250. /**
  251. * Delete spawn.
  252. * @param spawnDat the spawn dat
  253. * @param updateDb the update db
  254. */
  255. public void deleteSpawn(L2Spawn spawnDat, boolean updateDb)
  256. {
  257. if (spawnDat == null)
  258. {
  259. return;
  260. }
  261. final int bossId = spawnDat.getId();
  262. if (!_spawns.containsKey(bossId))
  263. {
  264. return;
  265. }
  266. SpawnTable.getInstance().deleteSpawn(spawnDat, false);
  267. _spawns.remove(bossId);
  268. if (_bosses.containsKey(bossId))
  269. {
  270. _bosses.remove(bossId);
  271. }
  272. if (_schedules.containsKey(bossId))
  273. {
  274. final ScheduledFuture<?> f = _schedules.remove(bossId);
  275. f.cancel(true);
  276. }
  277. if (_storedInfo.containsKey(bossId))
  278. {
  279. _storedInfo.remove(bossId);
  280. }
  281. if (updateDb)
  282. {
  283. try (Connection con = L2DatabaseFactory.getInstance().getConnection();
  284. PreparedStatement statement = con.prepareStatement("DELETE FROM raidboss_spawnlist WHERE boss_id=?"))
  285. {
  286. statement.setInt(1, bossId);
  287. statement.execute();
  288. }
  289. catch (Exception e)
  290. {
  291. // problem with deleting spawn
  292. _log.log(Level.WARNING, getClass().getSimpleName() + ": Could not remove raidboss #" + bossId + " from DB: " + e.getMessage(), e);
  293. }
  294. }
  295. }
  296. /**
  297. * Update database.
  298. */
  299. private void updateDb()
  300. {
  301. try (Connection con = L2DatabaseFactory.getInstance().getConnection();
  302. PreparedStatement statement = con.prepareStatement("UPDATE raidboss_spawnlist SET respawn_time = ?, currentHP = ?, currentMP = ? WHERE boss_id = ?"))
  303. {
  304. for (Integer bossId : _storedInfo.keySet())
  305. {
  306. if (bossId == null)
  307. {
  308. continue;
  309. }
  310. L2RaidBossInstance boss = _bosses.get(bossId);
  311. if (boss == null)
  312. {
  313. continue;
  314. }
  315. if (boss.getRaidStatus().equals(StatusEnum.ALIVE))
  316. {
  317. updateStatus(boss, false);
  318. }
  319. StatsSet info = _storedInfo.get(bossId);
  320. if (info == null)
  321. {
  322. continue;
  323. }
  324. try
  325. {
  326. statement.setLong(1, info.getLong("respawnTime"));
  327. statement.setDouble(2, info.getDouble("currentHP"));
  328. statement.setDouble(3, info.getDouble("currentMP"));
  329. statement.setInt(4, bossId);
  330. statement.executeUpdate();
  331. statement.clearParameters();
  332. }
  333. catch (SQLException e)
  334. {
  335. _log.log(Level.WARNING, getClass().getSimpleName() + ": Couldnt update raidboss_spawnlist table " + e.getMessage(), e);
  336. }
  337. }
  338. }
  339. catch (SQLException e)
  340. {
  341. _log.log(Level.WARNING, getClass().getSimpleName() + ": SQL error while updating RaidBoss spawn to database: " + e.getMessage(), e);
  342. }
  343. }
  344. /**
  345. * Gets the all raid boss status.
  346. * @return the all raid boss status
  347. */
  348. public String[] getAllRaidBossStatus()
  349. {
  350. final String[] msg = new String[(_bosses == null) ? 0 : _bosses.size()];
  351. if (_bosses == null)
  352. {
  353. msg[0] = "None";
  354. return msg;
  355. }
  356. int index = 0;
  357. for (int i : _bosses.keySet())
  358. {
  359. L2RaidBossInstance boss = _bosses.get(i);
  360. msg[index++] = boss.getName() + ": " + boss.getRaidStatus().name();
  361. }
  362. return msg;
  363. }
  364. /**
  365. * Gets the raid boss status.
  366. * @param bossId the boss id
  367. * @return the raid boss status
  368. */
  369. public String getRaidBossStatus(int bossId)
  370. {
  371. String msg = "RaidBoss Status..." + Config.EOL;
  372. if (_bosses == null)
  373. {
  374. msg += "None";
  375. return msg;
  376. }
  377. if (_bosses.containsKey(bossId))
  378. {
  379. final L2RaidBossInstance boss = _bosses.get(bossId);
  380. msg += boss.getName() + ": " + boss.getRaidStatus().name();
  381. }
  382. return msg;
  383. }
  384. /**
  385. * Gets the raid boss status id.
  386. * @param bossId the boss id
  387. * @return the raid boss status id
  388. */
  389. public StatusEnum getRaidBossStatusId(int bossId)
  390. {
  391. if (_bosses.containsKey(bossId))
  392. {
  393. return _bosses.get(bossId).getRaidStatus();
  394. }
  395. else if (_schedules.containsKey(bossId))
  396. {
  397. return StatusEnum.DEAD;
  398. }
  399. else
  400. {
  401. return StatusEnum.UNDEFINED;
  402. }
  403. }
  404. /**
  405. * Notify spawn night boss.
  406. * @param raidboss the raidboss
  407. */
  408. public void notifySpawnNightBoss(L2RaidBossInstance raidboss)
  409. {
  410. final StatsSet info = new StatsSet();
  411. info.set("currentHP", raidboss.getCurrentHp());
  412. info.set("currentMP", raidboss.getCurrentMp());
  413. info.set("respawnTime", 0L);
  414. raidboss.setRaidStatus(StatusEnum.ALIVE);
  415. _storedInfo.put(raidboss.getId(), info);
  416. _log.info(getClass().getSimpleName() + ": Spawning Night Raid Boss " + raidboss.getName());
  417. _bosses.put(raidboss.getId(), raidboss);
  418. }
  419. /**
  420. * Checks if the boss is defined.
  421. * @param bossId the boss id
  422. * @return {@code true} if is defined
  423. */
  424. public boolean isDefined(int bossId)
  425. {
  426. return _spawns.containsKey(bossId);
  427. }
  428. /**
  429. * Gets the bosses.
  430. * @return the bosses
  431. */
  432. public Map<Integer, L2RaidBossInstance> getBosses()
  433. {
  434. return _bosses;
  435. }
  436. /**
  437. * Gets the spawns.
  438. * @return the spawns
  439. */
  440. public Map<Integer, L2Spawn> getSpawns()
  441. {
  442. return _spawns;
  443. }
  444. /**
  445. * Gets the stored info.
  446. * @return the stored info
  447. */
  448. public Map<Integer, StatsSet> getStoredInfo()
  449. {
  450. return _storedInfo;
  451. }
  452. /**
  453. * Saves and clears the raid bosses status, including all schedules.
  454. */
  455. public void cleanUp()
  456. {
  457. updateDb();
  458. _bosses.clear();
  459. if (_schedules != null)
  460. {
  461. for (Integer bossId : _schedules.keySet())
  462. {
  463. ScheduledFuture<?> f = _schedules.get(bossId);
  464. f.cancel(true);
  465. }
  466. _schedules.clear();
  467. }
  468. _storedInfo.clear();
  469. _spawns.clear();
  470. }
  471. /**
  472. * Gets the single instance of RaidBossSpawnManager.
  473. * @return single instance of RaidBossSpawnManager
  474. */
  475. public static RaidBossSpawnManager getInstance()
  476. {
  477. return SingletonHolder._instance;
  478. }
  479. private static class SingletonHolder
  480. {
  481. protected static final RaidBossSpawnManager _instance = new RaidBossSpawnManager();
  482. }
  483. }