/* * This file is part of the L2J Mobius project. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package com.l2jmobius.gameserver.instancemanager; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import java.util.logging.Logger; import com.l2jmobius.commons.database.DatabaseFactory; import com.l2jmobius.gameserver.ThreadPoolManager; import com.l2jmobius.gameserver.data.xml.impl.NpcData; import com.l2jmobius.gameserver.instancemanager.tasks.GrandBossManagerStoreTask; import com.l2jmobius.gameserver.model.StatsSet; import com.l2jmobius.gameserver.model.actor.instance.L2GrandBossInstance; import com.l2jmobius.gameserver.model.interfaces.IStorable; /** * Grand Boss manager. * @author DaRkRaGe Revised by Emperorc */ public final class GrandBossManager implements IStorable { // SQL queries private static final String UPDATE_GRAND_BOSS_DATA = "UPDATE grandboss_data set loc_x = ?, loc_y = ?, loc_z = ?, heading = ?, respawn_time = ?, currentHP = ?, currentMP = ?, status = ? where boss_id = ?"; private static final String UPDATE_GRAND_BOSS_DATA2 = "UPDATE grandboss_data set status = ? where boss_id = ?"; protected static Logger _log = Logger.getLogger(GrandBossManager.class.getName()); protected static Map _bosses = new ConcurrentHashMap<>(); protected static Map _storedInfo = new HashMap<>(); private final Map _bossStatus = new HashMap<>(); protected GrandBossManager() { init(); } private void init() { try (Connection con = DatabaseFactory.getInstance().getConnection(); Statement s = con.createStatement(); ResultSet rs = s.executeQuery("SELECT * from grandboss_data ORDER BY boss_id")) { while (rs.next()) { // Read all info from DB, and store it for AI to read and decide what to do // faster than accessing DB in real time final StatsSet info = new StatsSet(); final int bossId = rs.getInt("boss_id"); info.set("loc_x", rs.getInt("loc_x")); info.set("loc_y", rs.getInt("loc_y")); info.set("loc_z", rs.getInt("loc_z")); info.set("heading", rs.getInt("heading")); info.set("respawn_time", rs.getLong("respawn_time")); info.set("currentHP", rs.getDouble("currentHP")); info.set("currentMP", rs.getDouble("currentMP")); final int status = rs.getInt("status"); _bossStatus.put(bossId, status); _storedInfo.put(bossId, info); _log.info(getClass().getSimpleName() + ": " + NpcData.getInstance().getTemplate(bossId).getName() + "(" + bossId + ") status is " + status); if (status > 0) { _log.info(getClass().getSimpleName() + ": Next spawn date of " + NpcData.getInstance().getTemplate(bossId).getName() + " is " + new Date(info.getLong("respawn_time"))); } } _log.info(getClass().getSimpleName() + ": Loaded " + _storedInfo.size() + " Instances."); } catch (SQLException e) { _log.log(Level.WARNING, getClass().getSimpleName() + ": Could not load grandboss_data table: " + e.getMessage(), e); } catch (Exception e) { _log.log(Level.WARNING, getClass().getSimpleName() + ": Error while initializing GrandBossManager: " + e.getMessage(), e); } ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new GrandBossManagerStoreTask(), 5 * 60 * 1000, 5 * 60 * 1000); } public int getBossStatus(int bossId) { return _bossStatus.get(bossId); } public void setBossStatus(int bossId, int status) { _bossStatus.put(bossId, status); _log.info(getClass().getSimpleName() + ": Updated " + NpcData.getInstance().getTemplate(bossId).getName() + "(" + bossId + ") status to " + status); updateDb(bossId, true); } /** * Adds a L2GrandBossInstance to the list of bosses. * @param boss */ public void addBoss(L2GrandBossInstance boss) { if (boss != null) { _bosses.put(boss.getId(), boss); } } public L2GrandBossInstance getBoss(int bossId) { return _bosses.get(bossId); } public StatsSet getStatsSet(int bossId) { return _storedInfo.get(bossId); } public void setStatsSet(int bossId, StatsSet info) { _storedInfo.put(bossId, info); updateDb(bossId, false); } @Override public boolean storeMe() { try (Connection con = DatabaseFactory.getInstance().getConnection()) { for (Entry e : _storedInfo.entrySet()) { final L2GrandBossInstance boss = _bosses.get(e.getKey()); final StatsSet info = e.getValue(); if ((boss == null) || (info == null)) { try (PreparedStatement update = con.prepareStatement(UPDATE_GRAND_BOSS_DATA2)) { update.setInt(1, _bossStatus.get(e.getKey())); update.setInt(2, e.getKey()); update.executeUpdate(); update.clearParameters(); } } else { try (PreparedStatement update = con.prepareStatement(UPDATE_GRAND_BOSS_DATA)) { update.setInt(1, boss.getX()); update.setInt(2, boss.getY()); update.setInt(3, boss.getZ()); update.setInt(4, boss.getHeading()); update.setLong(5, info.getLong("respawn_time")); double hp = boss.getCurrentHp(); double mp = boss.getCurrentMp(); if (boss.isDead()) { hp = boss.getMaxHp(); mp = boss.getMaxMp(); } update.setDouble(6, hp); update.setDouble(7, mp); update.setInt(8, _bossStatus.get(e.getKey())); update.setInt(9, e.getKey()); update.executeUpdate(); update.clearParameters(); } } } } catch (SQLException e) { _log.log(Level.WARNING, "Couldn't store grandbosses to database: " + e.getMessage(), e); return false; } return true; } private void updateDb(int bossId, boolean statusOnly) { try (Connection con = DatabaseFactory.getInstance().getConnection()) { final L2GrandBossInstance boss = _bosses.get(bossId); final StatsSet info = _storedInfo.get(bossId); if (statusOnly || (boss == null) || (info == null)) { try (PreparedStatement ps = con.prepareStatement(UPDATE_GRAND_BOSS_DATA2)) { ps.setInt(1, _bossStatus.get(bossId)); ps.setInt(2, bossId); ps.executeUpdate(); } } else { try (PreparedStatement ps = con.prepareStatement(UPDATE_GRAND_BOSS_DATA)) { ps.setInt(1, boss.getX()); ps.setInt(2, boss.getY()); ps.setInt(3, boss.getZ()); ps.setInt(4, boss.getHeading()); ps.setLong(5, info.getLong("respawn_time")); double hp = boss.getCurrentHp(); double mp = boss.getCurrentMp(); if (boss.isDead()) { hp = boss.getMaxHp(); mp = boss.getMaxMp(); } ps.setDouble(6, hp); ps.setDouble(7, mp); ps.setInt(8, _bossStatus.get(bossId)); ps.setInt(9, bossId); ps.executeUpdate(); } } } catch (SQLException e) { _log.log(Level.WARNING, "Couldn't update grandbosses to database:" + e.getMessage(), e); } } /** * Saves all Grand Boss info and then clears all info from memory, including all schedules. */ public void cleanUp() { storeMe(); _bosses.clear(); _storedInfo.clear(); _bossStatus.clear(); } /** * Gets the single instance of {@code GrandBossManager}. * @return single instance of {@code GrandBossManager} */ public static GrandBossManager getInstance() { return SingletonHolder._instance; } private static class SingletonHolder { protected static final GrandBossManager _instance = new GrandBossManager(); } }