/
tofu
/
l2interlude
Обзор
Документация
Войти
/
tofu
/
l2interlude
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
develop
src/main/java/com/l2jserver/gameserver/instancemanager/RaidBossSpawnManager.java
458 строк
13 KB
Zoey76
Copyright Update
01 янв 2025, 07:57
01 янв 2025, 07:57
16130a5
Код
Авторство
О чём код?
/* * Copyright © 2004-2025 L2J Server * * This file is part of L2J Server. * * L2J Server 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. * * L2J Server 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 <http://www.gnu.org/licenses/>. */ package com.l2jserver.gameserver.instancemanager; import static com.l2jserver.gameserver.config.Configuration.npc; import java.util.Calendar; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledFuture; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.l2jserver.commons.database.ConnectionFactory; import com.l2jserver.commons.util.Rnd; import com.l2jserver.gameserver.ThreadPoolManager; import com.l2jserver.gameserver.config.Configuration; import com.l2jserver.gameserver.datatables.SpawnTable; import com.l2jserver.gameserver.model.L2Spawn; import com.l2jserver.gameserver.model.StatsSet; import com.l2jserver.gameserver.model.actor.instance.L2RaidBossInstance; /** * Raid Boss spawn manager. * @author godson */ public class RaidBossSpawnManager { private static final Logger LOG = LoggerFactory.getLogger(RaidBossSpawnManager.class); private static final int EILHALDER_VON_HELLMANN = 25328; protected static final Map<Integer, L2RaidBossInstance> _bosses = new ConcurrentHashMap<>(); protected static final Map<Integer, L2Spawn> _spawns = new ConcurrentHashMap<>(); protected static final Map<Integer, StatsSet> _storedInfo = new ConcurrentHashMap<>(); protected static final Map<Integer, ScheduledFuture<?>> _schedules = new ConcurrentHashMap<>(); public enum StatusEnum { ALIVE, DEAD, UNDEFINED } protected RaidBossSpawnManager() { load(); } public void load() { LOG.info("Spawning raid bosses..."); _bosses.clear(); _spawns.clear(); _storedInfo.clear(); _schedules.clear(); try (var con = ConnectionFactory.getInstance().getConnection(); var s = con.createStatement(); var rs = s.executeQuery("SELECT * FROM raidboss_spawnlist ORDER BY boss_id")) { while (rs.next()) { final L2Spawn spawnDat = new L2Spawn(rs.getInt("boss_id")); spawnDat.setX(rs.getInt("loc_x")); spawnDat.setY(rs.getInt("loc_y")); spawnDat.setZ(rs.getInt("loc_z")); spawnDat.setAmount(rs.getInt("amount")); spawnDat.setHeading(rs.getInt("heading")); spawnDat.setRespawnDelay(rs.getInt("respawn_delay"), rs.getInt("respawn_random")); addNewSpawn(spawnDat, rs.getLong("respawn_time"), rs.getDouble("currentHP"), rs.getDouble("currentMP"), false); } LOG.info("Loaded {} bosses.", _bosses.size()); LOG.info("Scheduled {} boss instances.", _schedules.size()); } catch (Exception ex) { LOG.warn("There has been an error while initializing raid boss spawn manager!", ex); } } private static class SpawnSchedule implements Runnable { private static final Logger LOG = LoggerFactory.getLogger(SpawnSchedule.class); private final int bossId; public SpawnSchedule(int npcId) { bossId = npcId; } @Override public void run() { L2RaidBossInstance raidBoss; if (bossId == EILHALDER_VON_HELLMANN) { raidBoss = DayNightSpawnManager.getInstance().handleBoss(_spawns.get(bossId)); } else { raidBoss = (L2RaidBossInstance) _spawns.get(bossId).doSpawn(); } if (raidBoss != null) { raidBoss.setRaidStatus(StatusEnum.ALIVE); final StatsSet info = new StatsSet(); info.set("currentHP", raidBoss.getCurrentHp()); info.set("currentMP", raidBoss.getCurrentMp()); info.set("respawnTime", 0L); _storedInfo.put(bossId, info); LOG.info("Spawning Raid Boss {}.", raidBoss.getName()); _bosses.put(bossId, raidBoss); } _schedules.remove(bossId); } } /** * Update status. * @param boss the boss * @param isBossDead the is boss dead */ public void updateStatus(L2RaidBossInstance boss, boolean isBossDead) { final StatsSet info = _storedInfo.get(boss.getId()); if (info == null) { return; } if (isBossDead) { boss.setRaidStatus(StatusEnum.DEAD); final int respawnMinDelay = (int) (boss.getSpawn().getRespawnMinDelay() * npc().getRaidMinRespawnMultiplier()); final int respawnMaxDelay = (int) (boss.getSpawn().getRespawnMaxDelay() * npc().getRaidMaxRespawnMultiplier()); final int respawnDelay = Rnd.get(respawnMinDelay, respawnMaxDelay); final long respawnTime = Calendar.getInstance().getTimeInMillis() + respawnDelay; info.set("currentHP", boss.getMaxHp()); info.set("currentMP", boss.getMaxMp()); info.set("respawnTime", respawnTime); if (!_schedules.containsKey(boss.getId()) && ((respawnMinDelay > 0) || (respawnMaxDelay > 0))) { final Calendar time = Calendar.getInstance(); time.setTimeInMillis(respawnTime); LOG.info("Updated {} respawn time to {}.", boss.getName(), time.getTime()); _schedules.put(boss.getId(), ThreadPoolManager.getInstance().scheduleGeneral(new SpawnSchedule(boss.getId()), respawnDelay)); updateDb(); } } else { boss.setRaidStatus(StatusEnum.ALIVE); info.set("currentHP", boss.getCurrentHp()); info.set("currentMP", boss.getCurrentMp()); info.set("respawnTime", 0L); } _storedInfo.put(boss.getId(), info); } /** * Adds the new spawn. * @param spawnDat the spawn dat * @param respawnTime the respawn time * @param currentHP the current hp * @param currentMP the current mp * @param storeInDb the store in db */ public void addNewSpawn(L2Spawn spawnDat, long respawnTime, double currentHP, double currentMP, boolean storeInDb) { if (spawnDat == null) { return; } if (_spawns.containsKey(spawnDat.getId())) { return; } final int bossId = spawnDat.getId(); final long time = Calendar.getInstance().getTimeInMillis(); SpawnTable.getInstance().addNewSpawn(spawnDat, false); if ((respawnTime == 0L) || (time > respawnTime)) { L2RaidBossInstance raidBoss; if (bossId == EILHALDER_VON_HELLMANN) { raidBoss = DayNightSpawnManager.getInstance().handleBoss(spawnDat); } else { raidBoss = (L2RaidBossInstance) spawnDat.doSpawn(); } if (raidBoss != null) { raidBoss.setCurrentHp(currentHP); raidBoss.setCurrentMp(currentMP); raidBoss.setRaidStatus(StatusEnum.ALIVE); _bosses.put(bossId, raidBoss); final StatsSet info = new StatsSet(); info.set("currentHP", currentHP); info.set("currentMP", currentMP); info.set("respawnTime", 0L); _storedInfo.put(bossId, info); } } else { final long spawnTime = respawnTime - Calendar.getInstance().getTimeInMillis(); _schedules.put(bossId, ThreadPoolManager.getInstance().scheduleGeneral(new SpawnSchedule(bossId), spawnTime)); } _spawns.put(bossId, spawnDat); if (storeInDb) { try (var con = ConnectionFactory.getInstance().getConnection(); var ps = con.prepareStatement("INSERT INTO raidboss_spawnlist (boss_id,amount,loc_x,loc_y,loc_z,heading,respawn_time,currentHp,currentMp) VALUES(?,?,?,?,?,?,?,?,?)")) { ps.setInt(1, spawnDat.getId()); ps.setInt(2, spawnDat.getAmount()); ps.setInt(3, spawnDat.getX()); ps.setInt(4, spawnDat.getY()); ps.setInt(5, spawnDat.getZ()); ps.setInt(6, spawnDat.getHeading()); ps.setLong(7, respawnTime); ps.setDouble(8, currentHP); ps.setDouble(9, currentMP); ps.execute(); } catch (Exception ex) { LOG.warn("Could not store raid boss Id {} in the DB!", bossId, ex); } } } /** * Delete spawn. * @param spawnDat the spawn dat * @param updateDb the update db */ public void deleteSpawn(L2Spawn spawnDat, boolean updateDb) { if (spawnDat == null) { return; } final int bossId = spawnDat.getId(); if (!_spawns.containsKey(bossId)) { return; } SpawnTable.getInstance().deleteSpawn(spawnDat, false); _spawns.remove(bossId); _bosses.remove(bossId); final ScheduledFuture<?> f = _schedules.remove(bossId); if (f != null) { f.cancel(true); } _storedInfo.remove(bossId); if (updateDb) { try (var con = ConnectionFactory.getInstance().getConnection(); var ps = con.prepareStatement("DELETE FROM raidboss_spawnlist WHERE boss_id=?")) { ps.setInt(1, bossId); ps.execute(); } catch (Exception ex) { LOG.warn("Could not remove raid boss Id {} from DB!", bossId, ex); } } } private void updateDb() { try (var con = ConnectionFactory.getInstance().getConnection(); var ps = con.prepareStatement("UPDATE raidboss_spawnlist SET respawn_time = ?, currentHP = ?, currentMP = ? WHERE boss_id = ?")) { for (Integer bossId : _storedInfo.keySet()) { if (bossId == null) { continue; } L2RaidBossInstance boss = _bosses.get(bossId); if (boss == null) { continue; } if (boss.getRaidStatus().equals(StatusEnum.ALIVE)) { updateStatus(boss, false); } StatsSet info = _storedInfo.get(bossId); if (info == null) { continue; } try { // TODO(Zoey76): Change this to use batch. ps.setLong(1, info.getLong("respawnTime")); ps.setDouble(2, info.getDouble("currentHP")); ps.setDouble(3, info.getDouble("currentMP")); ps.setInt(4, bossId); ps.executeUpdate(); ps.clearParameters(); } catch (Exception ex) { LOG.warn("Could not update raid boss spawn list table!", ex); } } } catch (Exception ex) { LOG.warn("SQL error while updating Raid Boss spawn to database!", ex); } } /** * Gets the all raid boss status. * @return the all raid boss status */ public String[] getAllRaidBossStatus() { final String[] msg = new String[Math.max(_bosses.size(), 1)]; if (_bosses.isEmpty()) { msg[0] = "None"; return msg; } int index = 0; for (var boss : _bosses.values()) { msg[index++] = boss.getName() + ": " + boss.getRaidStatus().name(); } return msg; } /** * Gets the raid boss status. * @param bossId the boss id * @return the raid boss status */ public String getRaidBossStatus(int bossId) { String msg = "RaidBoss Status..." + Configuration.EOL; if (_bosses == null) { msg += "None"; return msg; } if (_bosses.containsKey(bossId)) { final L2RaidBossInstance boss = _bosses.get(bossId); msg += boss.getName() + ": " + boss.getRaidStatus().name(); } return msg; } /** * Gets the raid boss status id. * @param bossId the boss id * @return the raid boss status id */ public StatusEnum getRaidBossStatusId(int bossId) { if (_bosses.containsKey(bossId)) { return _bosses.get(bossId).getRaidStatus(); } else if (_schedules.containsKey(bossId)) { return StatusEnum.DEAD; } else { return StatusEnum.UNDEFINED; } } /** * Notify spawn night boss. * @param raidBoss the raid boss */ public void notifySpawnNightBoss(L2RaidBossInstance raidBoss) { final StatsSet info = new StatsSet(); info.set("currentHP", raidBoss.getCurrentHp()); info.set("currentMP", raidBoss.getCurrentMp()); info.set("respawnTime", 0L); raidBoss.setRaidStatus(StatusEnum.ALIVE); _storedInfo.put(raidBoss.getId(), info); LOG.info("Spawning Night Raid Boss {}.", raidBoss.getName()); _bosses.put(raidBoss.getId(), raidBoss); } /** * Checks if the boss is defined. * @param bossId the boss id * @return {@code true} if is defined */ public boolean isDefined(int bossId) { return _spawns.containsKey(bossId); } /** * Gets the bosses. * @return the bosses */ public Map<Integer, L2RaidBossInstance> getBosses() { return _bosses; } /** * Gets the spawns. * @return the spawns */ public Map<Integer, L2Spawn> getSpawns() { return _spawns; } /** * Gets the stored info. * @return the stored info */ public Map<Integer, StatsSet> getStoredInfo() { return _storedInfo; } /** * Saves and clears the raid bosses status, including all schedules. */ public void cleanUp() { updateDb(); _bosses.clear(); if (_schedules != null) { for (Integer bossId : _schedules.keySet()) { ScheduledFuture<?> f = _schedules.get(bossId); f.cancel(true); } _schedules.clear(); } _storedInfo.clear(); _spawns.clear(); } public static RaidBossSpawnManager getInstance() { return SingletonHolder.INSTANCE; } private static class SingletonHolder { protected static final RaidBossSpawnManager INSTANCE = new RaidBossSpawnManager(); } }