l2j_mobius/trunk/java/com/l2jmobius/gameserver/instancemanager/DBSpawnManager.java
2016-06-12 01:34:09 +00:00

646 lines
16 KiB
Java

/*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.instancemanager;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.l2jmobius.Config;
import com.l2jmobius.commons.database.DatabaseFactory;
import com.l2jmobius.commons.util.Rnd;
import com.l2jmobius.gameserver.ThreadPoolManager;
import com.l2jmobius.gameserver.data.xml.impl.NpcData;
import com.l2jmobius.gameserver.data.xml.impl.SpawnsData;
import com.l2jmobius.gameserver.datatables.SpawnTable;
import com.l2jmobius.gameserver.model.L2Spawn;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.actor.templates.L2NpcTemplate;
import com.l2jmobius.gameserver.model.spawns.NpcSpawnTemplate;
import com.l2jmobius.gameserver.util.Util;
/**
* Database spawn manager.
* @author godson, UnAfraid
*/
public class DBSpawnManager
{
private static final Logger LOGGER = Logger.getLogger(DBSpawnManager.class.getName());
protected final Map<Integer, L2Npc> _npcs = new ConcurrentHashMap<>();
protected final Map<Integer, L2Spawn> _spawns = new ConcurrentHashMap<>();
protected final Map<Integer, StatsSet> _storedInfo = new ConcurrentHashMap<>();
protected final Map<Integer, ScheduledFuture<?>> _schedules = new ConcurrentHashMap<>();
public static enum DBStatusType
{
ALIVE,
DEAD,
UNDEFINED
}
/**
* Instantiates a new raid npc spawn manager.
*/
protected DBSpawnManager()
{
load();
}
/**
* Load.
*/
public void load()
{
_npcs.clear();
_spawns.clear();
_storedInfo.clear();
_schedules.clear();
try (Connection con = DatabaseFactory.getInstance().getConnection();
PreparedStatement statement = con.prepareStatement("SELECT * FROM npc_respawns");
ResultSet rset = statement.executeQuery())
{
while (rset.next())
{
final L2NpcTemplate template = getValidTemplate(rset.getInt("id"));
if (template != null)
{
final L2Spawn spawn = new L2Spawn(template);
spawn.setX(rset.getInt("x"));
spawn.setY(rset.getInt("y"));
spawn.setZ(rset.getInt("z"));
spawn.setAmount(1);
spawn.setHeading(rset.getInt("heading"));
final List<NpcSpawnTemplate> spawns = SpawnsData.getInstance().getSpawns(npc -> (npc.getId() == template.getId()) && npc.hasDBSave());
if (spawns.isEmpty())
{
LOGGER.warning(getClass().getSimpleName() + ": Couldn't find spawn declaration for npc: " + template.getId() + " - " + template.getName());
deleteSpawn(spawn, true);
continue;
}
else if (spawns.size() > 1)
{
LOGGER.warning(getClass().getSimpleName() + ": Found multiple database spawns for npc: " + template.getId() + " - " + template.getName() + " " + spawns);
continue;
}
final NpcSpawnTemplate spawnTemplate = spawns.get(0);
spawn.setSpawnTemplate(spawnTemplate);
int respawn = 0, respawnRandom = 0;
if (spawnTemplate.getRespawnTime() != null)
{
respawn = (int) spawnTemplate.getRespawnTime().getSeconds();
}
if (spawnTemplate.getRespawnTimeRandom() != null)
{
respawnRandom = (int) spawnTemplate.getRespawnTimeRandom().getSeconds();
}
if (respawn > 0)
{
spawn.setRespawnDelay(respawn, respawnRandom);
spawn.startRespawn();
}
else
{
LOGGER.warning(getClass().getSimpleName() + ": Found database spawns without respawn for npc: " + template.getId() + " - " + template.getName() + " " + spawnTemplate);
continue;
}
addNewSpawn(spawn, rset.getLong("respawnTime"), rset.getDouble("currentHp"), rset.getDouble("currentMp"), false);
}
else
{
LOGGER.warning(getClass().getSimpleName() + ": Could not load npc #" + rset.getInt("id") + " from DB");
}
}
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _npcs.size() + " Instances");
LOGGER.info(getClass().getSimpleName() + ": Scheduled " + _schedules.size() + " Instances");
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Couldnt load npc_respawns table", e);
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Error while initializing DBSpawnManager: ", e);
}
}
private class SpawnSchedule implements Runnable
{
private final Logger LOGGER = Logger.getLogger(SpawnSchedule.class.getName());
private final int _npcId;
/**
* Instantiates a new spawn schedule.
* @param npcId the npc id
*/
public SpawnSchedule(int npcId)
{
_npcId = npcId;
}
@Override
public void run()
{
final L2Npc npc = _spawns.get(_npcId).doSpawn();
if (npc != null)
{
npc.setDBStatus(DBStatusType.ALIVE);
final StatsSet info = new StatsSet();
info.set("currentHP", npc.getCurrentHp());
info.set("currentMP", npc.getCurrentMp());
info.set("respawnTime", 0L);
_storedInfo.put(_npcId, info);
_npcs.put(_npcId, npc);
LOGGER.info(getClass().getSimpleName() + ": Spawning NPC " + npc.getName());
}
_schedules.remove(_npcId);
}
}
/**
* Update status.
* @param npc the npc
* @param isNpcDead the is npc dead
*/
public void updateStatus(L2Npc npc, boolean isNpcDead)
{
final StatsSet info = _storedInfo.get(npc.getId());
if (info == null)
{
return;
}
if (isNpcDead)
{
npc.setDBStatus(DBStatusType.DEAD);
final int respawnMinDelay = (int) (npc.getSpawn().getRespawnMinDelay() * Config.RAID_MIN_RESPAWN_MULTIPLIER);
final int respawnMaxDelay = (int) (npc.getSpawn().getRespawnMaxDelay() * Config.RAID_MAX_RESPAWN_MULTIPLIER);
final int respawnDelay = Rnd.get(respawnMinDelay, respawnMaxDelay);
final long respawnTime = System.currentTimeMillis() + respawnDelay;
info.set("currentHP", npc.getMaxHp());
info.set("currentMP", npc.getMaxMp());
info.set("respawnTime", respawnTime);
if (!_schedules.containsKey(npc.getId()) && ((respawnMinDelay > 0) || (respawnMaxDelay > 0)))
{
LOGGER.info(getClass().getSimpleName() + ": Updated " + npc.getName() + " respawn time to " + Util.formatDate(new Date(respawnTime), "dd.MM.yyyy HH:mm"));
_schedules.put(npc.getId(), ThreadPoolManager.getInstance().scheduleGeneral(new SpawnSchedule(npc.getId()), respawnDelay));
updateDb();
}
}
else
{
npc.setDBStatus(DBStatusType.ALIVE);
info.set("currentHP", npc.getCurrentHp());
info.set("currentMP", npc.getCurrentMp());
info.set("respawnTime", 0L);
}
_storedInfo.put(npc.getId(), info);
}
/**
* Adds the new spawn.
* @param spawn 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 spawn, long respawnTime, double currentHP, double currentMP, boolean storeInDb)
{
if (spawn == null)
{
return;
}
if (_spawns.containsKey(spawn.getId()))
{
return;
}
final int npcId = spawn.getId();
final long time = System.currentTimeMillis();
SpawnTable.getInstance().addNewSpawn(spawn, false);
if ((respawnTime == 0L) || (time > respawnTime))
{
final L2Npc npc = spawn.doSpawn();
if (npc != null)
{
npc.setCurrentHp(currentHP);
npc.setCurrentMp(currentMP);
npc.setDBStatus(DBStatusType.ALIVE);
_npcs.put(npcId, npc);
final StatsSet info = new StatsSet();
info.set("currentHP", currentHP);
info.set("currentMP", currentMP);
info.set("respawnTime", 0L);
_storedInfo.put(npcId, info);
}
}
else
{
final long spawnTime = respawnTime - System.currentTimeMillis();
_schedules.put(npcId, ThreadPoolManager.getInstance().scheduleGeneral(new SpawnSchedule(npcId), spawnTime));
}
_spawns.put(npcId, spawn);
if (storeInDb)
{
try (Connection con = DatabaseFactory.getInstance().getConnection();
PreparedStatement statement = con.prepareStatement("INSERT INTO npc_respawns (id, x, y, z, heading, respawnTime, currentHp, currentMp) VALUES(?, ?, ?, ?, ?, ?, ?, ?)"))
{
statement.setInt(1, spawn.getId());
statement.setInt(2, spawn.getX());
statement.setInt(3, spawn.getY());
statement.setInt(4, spawn.getZ());
statement.setInt(5, spawn.getHeading());
statement.setLong(6, respawnTime);
statement.setDouble(7, currentHP);
statement.setDouble(8, currentMP);
statement.execute();
}
catch (Exception e)
{
// problem with storing spawn
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Could not store npc #" + npcId + " in the DB: ", e);
}
}
}
public void addNewSpawn(L2Spawn spawn, boolean storeInDb)
{
if (spawn == null)
{
return;
}
final int npcId = spawn.getId();
if (_spawns.containsKey(npcId))
{
return;
}
SpawnTable.getInstance().addNewSpawn(spawn, false);
final L2Npc npc = spawn.doSpawn();
if (npc == null)
{
throw new NullPointerException();
}
npc.setDBStatus(DBStatusType.ALIVE);
final StatsSet info = new StatsSet();
info.set("currentHP", npc.getMaxHp());
info.set("currentMP", npc.getMaxMp());
info.set("respawnTime", 0L);
_npcs.put(npcId, npc);
_storedInfo.put(npcId, info);
_spawns.put(npcId, spawn);
if (storeInDb)
{
try (Connection con = DatabaseFactory.getInstance().getConnection();
PreparedStatement statement = con.prepareStatement("INSERT INTO npc_respawns (id, x, y, z, heading, respawnTime, currentHp, currentMp) VALUES(?, ?, ?, ?, ?, ?, ?, ?)"))
{
statement.setInt(1, spawn.getId());
statement.setInt(2, spawn.getX());
statement.setInt(3, spawn.getY());
statement.setInt(4, spawn.getZ());
statement.setInt(5, spawn.getHeading());
statement.setLong(6, 0);
statement.setDouble(7, npc.getMaxHp());
statement.setDouble(8, npc.getMaxMp());
statement.execute();
}
catch (Exception e)
{
// problem with storing spawn
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Could not store npc #" + npcId + " in the DB: ", e);
}
}
}
/**
* Delete spawn.
* @param spawn the spawn dat
* @param updateDb the update db
*/
public void deleteSpawn(L2Spawn spawn, boolean updateDb)
{
if (spawn == null)
{
return;
}
final int npcId = spawn.getId();
if (!_spawns.containsKey(npcId))
{
return;
}
SpawnTable.getInstance().deleteSpawn(spawn, false);
_spawns.remove(npcId);
if (_npcs.containsKey(npcId))
{
_npcs.remove(npcId);
}
if (_schedules.containsKey(npcId))
{
final ScheduledFuture<?> f = _schedules.remove(npcId);
f.cancel(true);
}
if (_storedInfo.containsKey(npcId))
{
_storedInfo.remove(npcId);
}
if (updateDb)
{
try (Connection con = DatabaseFactory.getInstance().getConnection();
PreparedStatement ps = con.prepareStatement("DELETE FROM npc_respawns WHERE id = ?"))
{
ps.setInt(1, npcId);
ps.execute();
}
catch (Exception e)
{
// problem with deleting spawn
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Could not remove npc #" + npcId + " from DB: ", e);
}
}
}
/**
* Update database.
*/
private void updateDb()
{
try (Connection con = DatabaseFactory.getInstance().getConnection();
PreparedStatement statement = con.prepareStatement("UPDATE npc_respawns SET respawnTime = ?, currentHP = ?, currentMP = ? WHERE id = ?"))
{
for (Integer npcId : _storedInfo.keySet())
{
if (npcId == null)
{
continue;
}
final L2Npc npc = _npcs.get(npcId);
if (npc == null)
{
continue;
}
if (npc.getDBStatus().equals(DBStatusType.ALIVE))
{
updateStatus(npc, false);
}
final StatsSet info = _storedInfo.get(npcId);
if (info == null)
{
continue;
}
try
{
statement.setLong(1, info.getLong("respawnTime"));
statement.setDouble(2, info.getDouble("currentHP"));
statement.setDouble(3, info.getDouble("currentMP"));
statement.setInt(4, npcId);
statement.executeUpdate();
statement.clearParameters();
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Couldnt update npc_respawns table ", e);
}
}
}
catch (SQLException e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": SQL error while updating database spawn to database: ", e);
}
}
/**
* Gets the all npc status.
* @return the all npc status
*/
public String[] getAllNpcsStatus()
{
final String[] msg = new String[(_npcs == null) ? 0 : _npcs.size()];
if (_npcs == null)
{
msg[0] = "None";
return msg;
}
int index = 0;
for (int i : _npcs.keySet())
{
final L2Npc npc = _npcs.get(i);
msg[index++] = npc.getName() + ": " + npc.getDBStatus().name();
}
return msg;
}
/**
* Gets the npc status.
* @param npcId the npc id
* @return the raid npc status
*/
public String getNpcsStatus(int npcId)
{
String msg = "NPC Status..." + Config.EOL;
if (_npcs == null)
{
msg += "None";
return msg;
}
if (_npcs.containsKey(npcId))
{
final L2Npc npc = _npcs.get(npcId);
msg += npc.getName() + ": " + npc.getDBStatus().name();
}
return msg;
}
/**
* Gets the raid npc status id.
* @param npcId the npc id
* @return the raid npc status id
*/
public DBStatusType getNpcStatusId(int npcId)
{
if (_npcs.containsKey(npcId))
{
return _npcs.get(npcId).getDBStatus();
}
else if (_schedules.containsKey(npcId))
{
return DBStatusType.DEAD;
}
else
{
return DBStatusType.UNDEFINED;
}
}
/**
* Gets the valid template.
* @param npcId the npc id
* @return the valid template
*/
public L2NpcTemplate getValidTemplate(int npcId)
{
return NpcData.getInstance().getTemplate(npcId);
}
/**
* Notify spawn night npc.
* @param npc the npc
*/
public void notifySpawnNightNpc(L2Npc npc)
{
final StatsSet info = new StatsSet();
info.set("currentHP", npc.getCurrentHp());
info.set("currentMP", npc.getCurrentMp());
info.set("respawnTime", 0L);
npc.setDBStatus(DBStatusType.ALIVE);
_storedInfo.put(npc.getId(), info);
_npcs.put(npc.getId(), npc);
}
/**
* Checks if the npc is defined.
* @param npcId the npc id
* @return {@code true} if is defined
*/
public boolean isDefined(int npcId)
{
return _spawns.containsKey(npcId);
}
/**
* Gets the npcs.
* @return the npcs
*/
public Map<Integer, L2Npc> getNpcs()
{
return _npcs;
}
/**
* 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 npces status, including all schedules.
*/
public void cleanUp()
{
updateDb();
_npcs.clear();
if (_schedules != null)
{
for (Integer npcId : _schedules.keySet())
{
final ScheduledFuture<?> f = _schedules.get(npcId);
f.cancel(true);
}
_schedules.clear();
}
_storedInfo.clear();
_spawns.clear();
}
/**
* Gets the single instance of DBSpawnManager.
* @return single instance of DBSpawnManager
*/
public static DBSpawnManager getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final DBSpawnManager _instance = new DBSpawnManager();
}
}