|
@@ -22,207 +22,150 @@ import java.sql.Connection;
|
|
|
import java.sql.PreparedStatement;
|
|
|
import java.sql.ResultSet;
|
|
|
import java.sql.Statement;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.Set;
|
|
|
import java.util.logging.Level;
|
|
|
import java.util.logging.Logger;
|
|
|
|
|
|
+import javolution.util.FastMap;
|
|
|
import javolution.util.FastSet;
|
|
|
|
|
|
import com.l2jserver.Config;
|
|
|
import com.l2jserver.L2DatabaseFactory;
|
|
|
import com.l2jserver.gameserver.instancemanager.DayNightSpawnManager;
|
|
|
import com.l2jserver.gameserver.model.L2Spawn;
|
|
|
-import com.l2jserver.gameserver.model.actor.L2Npc;
|
|
|
-import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
|
|
|
import com.l2jserver.gameserver.model.actor.templates.L2NpcTemplate;
|
|
|
+import com.l2jserver.gameserver.model.interfaces.IL2Procedure;
|
|
|
|
|
|
/**
|
|
|
- * @author Nightmare
|
|
|
+ * Spawn data retriever.
|
|
|
+ * @author Zoey76
|
|
|
*/
|
|
|
-public class SpawnTable
|
|
|
+public final class SpawnTable
|
|
|
{
|
|
|
private static final Logger _log = Logger.getLogger(SpawnTable.class.getName());
|
|
|
+ // SQL
|
|
|
+ private static final String SELECT_SPAWNS = "SELECT count, npc_templateid, locx, locy, locz, heading, respawn_delay, respawn_random, loc_id, periodOfDay FROM spawnlist";
|
|
|
+ private static final String SELECT_CUSTOM_SPAWNS = "SELECT count, npc_templateid, locx, locy, locz, heading, respawn_delay, respawn_random, loc_id, periodOfDay FROM custom_spawnlist";
|
|
|
|
|
|
- private final FastSet<L2Spawn> _spawntable = new FastSet<>();
|
|
|
- private int _npcSpawnCount;
|
|
|
- private int _customSpawnCount;
|
|
|
+ private static final Map<Integer, Set<L2Spawn>> _spawnTable = new FastMap<Integer, Set<L2Spawn>>().shared();
|
|
|
|
|
|
protected SpawnTable()
|
|
|
{
|
|
|
- _spawntable.shared();
|
|
|
- if (!Config.ALT_DEV_NO_SPAWNS)
|
|
|
- {
|
|
|
- fillSpawnTable();
|
|
|
- }
|
|
|
+ load();
|
|
|
}
|
|
|
|
|
|
- public FastSet<L2Spawn> getSpawnTable()
|
|
|
+ /**
|
|
|
+ * Wrapper to load all spawns.
|
|
|
+ */
|
|
|
+ public void load()
|
|
|
{
|
|
|
- return _spawntable;
|
|
|
+ if (!Config.ALT_DEV_NO_SPAWNS)
|
|
|
+ {
|
|
|
+ fillSpawnTable(false);
|
|
|
+ final int spawnCount = _spawnTable.size();
|
|
|
+ _log.info(getClass().getSimpleName() + ": Loaded " + spawnCount + " npc spawns.");
|
|
|
+ if (Config.CUSTOM_SPAWNLIST_TABLE)
|
|
|
+ {
|
|
|
+ fillSpawnTable(true);
|
|
|
+ _log.info(getClass().getSimpleName() + ": Loaded " + (_spawnTable.size() - spawnCount) + " custom npc spawns.");
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- private void fillSpawnTable()
|
|
|
+ private int fillSpawnTable(boolean isCustom)
|
|
|
{
|
|
|
+ int npcSpawnCount = 0;
|
|
|
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
|
|
|
Statement s = con.createStatement();
|
|
|
- ResultSet rs = s.executeQuery("SELECT count, npc_templateid, locx, locy, locz, heading, respawn_delay, respawn_random, loc_id, periodOfDay FROM spawnlist"))
|
|
|
+ ResultSet rs = s.executeQuery(isCustom ? SELECT_CUSTOM_SPAWNS : SELECT_SPAWNS))
|
|
|
{
|
|
|
- L2Spawn spawnDat;
|
|
|
- L2NpcTemplate template1;
|
|
|
+ L2Spawn spawn;
|
|
|
+ L2NpcTemplate npcTemplate;
|
|
|
+ int npcId;
|
|
|
while (rs.next())
|
|
|
{
|
|
|
- template1 = NpcTable.getInstance().getTemplate(rs.getInt("npc_templateid"));
|
|
|
- if (template1 != null)
|
|
|
+ npcId = rs.getInt("npc_templateid");
|
|
|
+ npcTemplate = NpcTable.getInstance().getTemplate(npcId);
|
|
|
+ if (npcTemplate == null)
|
|
|
{
|
|
|
- if (template1.isType("L2SiegeGuard"))
|
|
|
- {
|
|
|
- // Don't spawn
|
|
|
- }
|
|
|
- else if (template1.isType("L2RaidBoss"))
|
|
|
- {
|
|
|
- // Don't spawn raidboss
|
|
|
- }
|
|
|
- else if (!Config.ALLOW_CLASS_MASTERS && template1.isType("L2ClassMaster"))
|
|
|
- {
|
|
|
- // Dont' spawn class masters
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- spawnDat = new L2Spawn(template1);
|
|
|
- spawnDat.setAmount(rs.getInt("count"));
|
|
|
- spawnDat.setLocx(rs.getInt("locx"));
|
|
|
- spawnDat.setLocy(rs.getInt("locy"));
|
|
|
- spawnDat.setLocz(rs.getInt("locz"));
|
|
|
- spawnDat.setHeading(rs.getInt("heading"));
|
|
|
- spawnDat.setRespawnDelay(rs.getInt("respawn_delay"), rs.getInt("respawn_random"));
|
|
|
- int loc_id = rs.getInt("loc_id");
|
|
|
- spawnDat.setLocation(loc_id);
|
|
|
-
|
|
|
- switch (rs.getInt("periodOfDay"))
|
|
|
- {
|
|
|
- case 0: // default
|
|
|
- _npcSpawnCount += spawnDat.init();
|
|
|
- break;
|
|
|
- case 1: // Day
|
|
|
- DayNightSpawnManager.getInstance().addDayCreature(spawnDat);
|
|
|
- _npcSpawnCount++;
|
|
|
- break;
|
|
|
- case 2: // Night
|
|
|
- DayNightSpawnManager.getInstance().addNightCreature(spawnDat);
|
|
|
- _npcSpawnCount++;
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- _spawntable.add(spawnDat);
|
|
|
- }
|
|
|
+ _log.warning(getClass().getSimpleName() + ": Data missing in NPC table for ID: " + npcId + ".");
|
|
|
+ continue;
|
|
|
}
|
|
|
- else
|
|
|
+
|
|
|
+ if (npcTemplate.isType("L2SiegeGuard") || npcTemplate.isType("L2RaidBoss") || (!Config.ALLOW_CLASS_MASTERS && npcTemplate.isType("L2ClassMaster")))
|
|
|
{
|
|
|
- _log.warning(getClass().getSimpleName() + ": Data missing in NPC table for ID: " + rs.getInt("npc_templateid") + ".");
|
|
|
+ // Don't spawn
|
|
|
+ continue;
|
|
|
}
|
|
|
+
|
|
|
+ spawn = new L2Spawn(npcTemplate);
|
|
|
+ spawn.setAmount(rs.getInt("count"));
|
|
|
+ spawn.setLocx(rs.getInt("locx"));
|
|
|
+ spawn.setLocy(rs.getInt("locy"));
|
|
|
+ spawn.setLocz(rs.getInt("locz"));
|
|
|
+ spawn.setHeading(rs.getInt("heading"));
|
|
|
+ spawn.setRespawnDelay(rs.getInt("respawn_delay"), rs.getInt("respawn_random"));
|
|
|
+ int loc_id = rs.getInt("loc_id");
|
|
|
+ spawn.setLocation(loc_id);
|
|
|
+
|
|
|
+ switch (rs.getInt("periodOfDay"))
|
|
|
+ {
|
|
|
+ case 0: // default
|
|
|
+ npcSpawnCount += spawn.init();
|
|
|
+ break;
|
|
|
+ case 1: // Day
|
|
|
+ DayNightSpawnManager.getInstance().addDayCreature(spawn);
|
|
|
+ npcSpawnCount++;
|
|
|
+ break;
|
|
|
+ case 2: // Night
|
|
|
+ DayNightSpawnManager.getInstance().addNightCreature(spawn);
|
|
|
+ npcSpawnCount++;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ addSpawn(spawn);
|
|
|
}
|
|
|
}
|
|
|
catch (Exception e)
|
|
|
{
|
|
|
- // problem with initializing spawn, go to next one
|
|
|
_log.log(Level.WARNING, getClass().getSimpleName() + ": Spawn could not be initialized: " + e.getMessage(), e);
|
|
|
}
|
|
|
-
|
|
|
- _log.info(getClass().getSimpleName() + ": Loaded " + _spawntable.size() + " npc spawns.");
|
|
|
-
|
|
|
- if (Config.CUSTOM_SPAWNLIST_TABLE)
|
|
|
+ return npcSpawnCount;
|
|
|
+ }
|
|
|
+
|
|
|
+ public Map<Integer, Set<L2Spawn>> getSpawnTable()
|
|
|
+ {
|
|
|
+ return _spawnTable;
|
|
|
+ }
|
|
|
+
|
|
|
+ public Set<L2Spawn> getSpawns(int npcId)
|
|
|
+ {
|
|
|
+ return _spawnTable.get(npcId);
|
|
|
+ }
|
|
|
+
|
|
|
+ public L2Spawn getFirstSpawn(int npcId)
|
|
|
+ {
|
|
|
+ if (_spawnTable.containsKey(npcId))
|
|
|
{
|
|
|
- try (Connection con = L2DatabaseFactory.getInstance().getConnection();
|
|
|
- Statement ps = con.createStatement();
|
|
|
- ResultSet rs = ps.executeQuery("SELECT count, npc_templateid, locx, locy, locz, heading, respawn_delay, respawn_random, loc_id, periodOfDay FROM custom_spawnlist"))
|
|
|
+ for (L2Spawn spawn : _spawnTable.get(npcId))
|
|
|
{
|
|
|
- L2Spawn spawnDat;
|
|
|
- L2NpcTemplate template1;
|
|
|
- while (rs.next())
|
|
|
+ if (spawn != null)
|
|
|
{
|
|
|
- template1 = NpcTable.getInstance().getTemplate(rs.getInt("npc_templateid"));
|
|
|
- if (template1 != null)
|
|
|
- {
|
|
|
- if (template1.isType("L2SiegeGuard"))
|
|
|
- {
|
|
|
- // Don't spawn
|
|
|
- }
|
|
|
- else if (template1.isType("L2RaidBoss"))
|
|
|
- {
|
|
|
- // Don't spawn raidboss
|
|
|
- }
|
|
|
- else if (!Config.ALLOW_CLASS_MASTERS && template1.isType("L2ClassMaster"))
|
|
|
- {
|
|
|
- // Dont' spawn class masters
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- spawnDat = new L2Spawn(template1);
|
|
|
- spawnDat.setAmount(rs.getInt("count"));
|
|
|
- spawnDat.setLocx(rs.getInt("locx"));
|
|
|
- spawnDat.setLocy(rs.getInt("locy"));
|
|
|
- spawnDat.setLocz(rs.getInt("locz"));
|
|
|
- spawnDat.setHeading(rs.getInt("heading"));
|
|
|
- spawnDat.setRespawnDelay(rs.getInt("respawn_delay"), rs.getInt("respawn_random"));
|
|
|
- spawnDat.setCustom(true);
|
|
|
- int loc_id = rs.getInt("loc_id");
|
|
|
- spawnDat.setLocation(loc_id);
|
|
|
-
|
|
|
- switch (rs.getInt("periodOfDay"))
|
|
|
- {
|
|
|
- case 0: // default
|
|
|
- _customSpawnCount += spawnDat.init();
|
|
|
- break;
|
|
|
- case 1: // Day
|
|
|
- DayNightSpawnManager.getInstance().addDayCreature(spawnDat);
|
|
|
- _customSpawnCount++;
|
|
|
- break;
|
|
|
- case 2: // Night
|
|
|
- DayNightSpawnManager.getInstance().addNightCreature(spawnDat);
|
|
|
- _customSpawnCount++;
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- _spawntable.add(spawnDat);
|
|
|
- }
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- _log.warning(getClass().getSimpleName() + ": Data missing in NPC table for ID: " + rs.getInt("npc_templateid") + ".");
|
|
|
- }
|
|
|
+ return spawn;
|
|
|
}
|
|
|
}
|
|
|
- catch (Exception e)
|
|
|
- {
|
|
|
- // problem with initializing spawn, go to next one
|
|
|
- _log.log(Level.WARNING, "CustomSpawnTable: Spawn could not be initialized: " + e.getMessage(), e);
|
|
|
- }
|
|
|
- _log.info(getClass().getSimpleName() + ": Loaded " + _customSpawnCount + " custom npc spawns.");
|
|
|
-
|
|
|
}
|
|
|
-
|
|
|
- if (Config.DEBUG)
|
|
|
- {
|
|
|
- _log.fine(getClass().getSimpleName() + ": Spawning completed, total number of NPCs in the world: " + (_npcSpawnCount + _customSpawnCount));
|
|
|
- }
|
|
|
-
|
|
|
+ return null;
|
|
|
}
|
|
|
|
|
|
public void addNewSpawn(L2Spawn spawn, boolean storeInDb)
|
|
|
{
|
|
|
- _spawntable.add(spawn);
|
|
|
+ addSpawn(spawn);
|
|
|
|
|
|
if (storeInDb)
|
|
|
{
|
|
|
- String spawnTable;
|
|
|
- if (spawn.isCustom() && Config.CUSTOM_SPAWNLIST_TABLE)
|
|
|
- {
|
|
|
- spawnTable = "custom_spawnlist";
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- spawnTable = "spawnlist";
|
|
|
- }
|
|
|
-
|
|
|
+ final String spawnTable = spawn.isCustom() && Config.CUSTOM_SPAWNLIST_TABLE ? "custom_spawnlist" : "spawnlist";
|
|
|
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
|
|
|
PreparedStatement insert = con.prepareStatement("INSERT INTO " + spawnTable + "(count,npc_templateid,locx,locy,locz,heading,respawn_delay,respawn_random,loc_id) values(?,?,?,?,?,?,?,?,?)"))
|
|
|
{
|
|
@@ -239,7 +182,6 @@ public class SpawnTable
|
|
|
}
|
|
|
catch (Exception e)
|
|
|
{
|
|
|
- // problem with storing spawn
|
|
|
_log.log(Level.WARNING, getClass().getSimpleName() + ": Could not store spawn in the DB:" + e.getMessage(), e);
|
|
|
}
|
|
|
}
|
|
@@ -247,7 +189,7 @@ public class SpawnTable
|
|
|
|
|
|
public void deleteSpawn(L2Spawn spawn, boolean updateDb)
|
|
|
{
|
|
|
- if (!_spawntable.remove(spawn))
|
|
|
+ if (!removeSpawn(spawn))
|
|
|
{
|
|
|
return;
|
|
|
}
|
|
@@ -266,67 +208,68 @@ public class SpawnTable
|
|
|
}
|
|
|
catch (Exception e)
|
|
|
{
|
|
|
- // problem with deleting spawn
|
|
|
_log.log(Level.WARNING, getClass().getSimpleName() + ": Spawn " + spawn + " could not be removed from DB: " + e.getMessage(), e);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // just wrapper
|
|
|
- public void reloadAll()
|
|
|
+ /**
|
|
|
+ * Add a spawn to the spawn set if present, otherwise add a spawn set and add the spawn to the newly created spawn set.
|
|
|
+ * @param spawn the NPC spawn to add
|
|
|
+ */
|
|
|
+ private void addSpawn(L2Spawn spawn)
|
|
|
{
|
|
|
- fillSpawnTable();
|
|
|
+ if (_spawnTable.containsKey(spawn.getNpcid()))
|
|
|
+ {
|
|
|
+ _spawnTable.get(spawn.getNpcid()).add(spawn);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ final FastSet<L2Spawn> test = new FastSet<L2Spawn>().shared();
|
|
|
+ test.add(spawn);
|
|
|
+ _spawnTable.put(spawn.getNpcid(), test);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Get all the spawn of a NPC.
|
|
|
- * @param activeChar
|
|
|
- * @param npcId
|
|
|
- * @param teleportIndex
|
|
|
- * @param showposition
|
|
|
+ * Remove a spawn from the spawn set, if the spawn set is empty, remove it as well.
|
|
|
+ * @param spawn the NPC spawn to remove
|
|
|
+ * @return {@code true} if the spawn was successfully removed, {@code false} otherwise
|
|
|
*/
|
|
|
- public void findNPCInstances(L2PcInstance activeChar, int npcId, int teleportIndex, boolean showposition)
|
|
|
+ private boolean removeSpawn(L2Spawn spawn)
|
|
|
{
|
|
|
- int index = 0;
|
|
|
- for (L2Spawn spawn : _spawntable)
|
|
|
+ if (_spawnTable.containsKey(spawn.getNpcid()))
|
|
|
{
|
|
|
-
|
|
|
- if (npcId == spawn.getNpcid())
|
|
|
+ final Set<L2Spawn> set = _spawnTable.get(spawn.getNpcid());
|
|
|
+ boolean removed = set.remove(spawn);
|
|
|
+ if (set.isEmpty())
|
|
|
{
|
|
|
- index++;
|
|
|
- L2Npc _npc = spawn.getLastSpawn();
|
|
|
- if (teleportIndex > -1)
|
|
|
- {
|
|
|
- if (teleportIndex == index)
|
|
|
- {
|
|
|
- if (showposition && (_npc != null))
|
|
|
- {
|
|
|
- activeChar.teleToLocation(_npc.getX(), _npc.getY(), _npc.getZ(), true);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- activeChar.teleToLocation(spawn.getLocx(), spawn.getLocy(), spawn.getLocz(), true);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- if (showposition && (_npc != null))
|
|
|
- {
|
|
|
- activeChar.sendMessage(index + " - " + spawn.getTemplate().getName() + " (" + spawn + "): " + _npc.getX() + " " + _npc.getY() + " " + _npc.getZ());
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- activeChar.sendMessage(index + " - " + spawn.getTemplate().getName() + " (" + spawn + "): " + spawn.getLocx() + " " + spawn.getLocy() + " " + spawn.getLocz());
|
|
|
- }
|
|
|
- }
|
|
|
+ _spawnTable.remove(spawn.getNpcid());
|
|
|
}
|
|
|
+ return removed;
|
|
|
}
|
|
|
-
|
|
|
- if (index == 0)
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Execute a procedure over all spawns.<br>
|
|
|
+ * <font size="4" color="red">Do not use it!</font>
|
|
|
+ * @param procedure the procedure to execute
|
|
|
+ * @return {@code true} if all procedures were executed, {@code false} otherwise
|
|
|
+ */
|
|
|
+ public boolean forEachSpawn(IL2Procedure<L2Spawn> procedure)
|
|
|
+ {
|
|
|
+ for (Set<L2Spawn> set : _spawnTable.values())
|
|
|
{
|
|
|
- activeChar.sendMessage(getClass().getSimpleName() + ": No current spawns found.");
|
|
|
+ for (L2Spawn spawn : set)
|
|
|
+ {
|
|
|
+ if (!procedure.execute(spawn))
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
+ return true;
|
|
|
}
|
|
|
|
|
|
public static SpawnTable getInstance()
|