Selaa lähdekoodia

BETA: New implementation of SpawnTable.
* Performance greatly improved.

Suggested by: jurchiks
Patch by: Zoey76

Zoey76 12 vuotta sitten
vanhempi
sitoutus
ec9ae7d173

+ 137 - 194
L2J_Server_BETA/java/com/l2jserver/gameserver/datatables/SpawnTable.java

@@ -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()

+ 17 - 9
L2J_Server_BETA/java/com/l2jserver/gameserver/model/entity/L2Event.java

@@ -36,8 +36,10 @@ import com.l2jserver.gameserver.datatables.SpawnTable;
 import com.l2jserver.gameserver.instancemanager.AntiFeedManager;
 import com.l2jserver.gameserver.model.L2Spawn;
 import com.l2jserver.gameserver.model.L2World;
+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;
 import com.l2jserver.gameserver.network.serverpackets.CharInfo;
 import com.l2jserver.gameserver.network.serverpackets.ExBrExtraUserInfo;
 import com.l2jserver.gameserver.network.serverpackets.MagicSkillUse;
@@ -202,20 +204,26 @@ public class L2Event
 		
 	}
 	
+	/**
+	 * Zoey76: TODO: Rewrite this in a way that doesn't iterate over all spawns.
+	 */
 	public static void unspawnEventNpcs()
 	{
-		// Its a little rough, but for sure it will remove every damn event NPC.
-		for (L2Spawn spawn : SpawnTable.getInstance().getSpawnTable())
+		SpawnTable.getInstance().forEachSpawn(new IL2Procedure<L2Spawn>()
 		{
-			if ((spawn.getLastSpawn() != null) && spawn.getLastSpawn().isEventMob())
+			@Override
+			public boolean execute(L2Spawn spawn)
 			{
-				spawn.getLastSpawn().deleteMe();
-				spawn.stopRespawn();
-				SpawnTable.getInstance().deleteSpawn(spawn, false);
+				L2Npc npc = spawn.getLastSpawn();
+				if ((npc != null) && npc.isEventMob())
+				{
+					npc.deleteMe();
+					spawn.stopRespawn();
+					SpawnTable.getInstance().deleteSpawn(spawn, false);
+				}
+				return true;
 			}
-		}
-		// for (L2Npc npc : _npcs)
-		// npc.deleteMe();
+		});
 	}
 	
 	/**

+ 3 - 12
L2J_Server_BETA/java/com/l2jserver/gameserver/model/olympiad/OlympiadAnnouncer.java

@@ -18,9 +18,7 @@
  */
 package com.l2jserver.gameserver.model.olympiad;
 
-import java.util.List;
-
-import javolution.util.FastList;
+import java.util.Set;
 
 import com.l2jserver.gameserver.datatables.SpawnTable;
 import com.l2jserver.gameserver.model.L2Spawn;
@@ -35,19 +33,12 @@ import com.l2jserver.gameserver.network.serverpackets.NpcSay;
 public final class OlympiadAnnouncer implements Runnable
 {
 	private static final int OLY_MANAGER = 31688;
-	
-	private final List<L2Spawn> _managers = new FastList<>();
+	private final Set<L2Spawn> _managers;
 	private int _currentStadium = 0;
 	
 	public OlympiadAnnouncer()
 	{
-		for (L2Spawn spawn : SpawnTable.getInstance().getSpawnTable())
-		{
-			if ((spawn != null) && (spawn.getNpcid() == OLY_MANAGER))
-			{
-				_managers.add(spawn);
-			}
-		}
+		_managers = SpawnTable.getInstance().getSpawns(OLY_MANAGER);
 	}
 	
 	@Override