123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742 |
- /*
- * Copyright (C) 2004-2015 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.model;
- import java.sql.Connection;
- import java.sql.PreparedStatement;
- import java.sql.ResultSet;
- import java.sql.Statement;
- import java.util.ArrayList;
- import java.util.LinkedList;
- import java.util.List;
- import java.util.Map;
- import java.util.Queue;
- import java.util.concurrent.ConcurrentHashMap;
- import java.util.concurrent.ConcurrentLinkedQueue;
- import java.util.concurrent.CopyOnWriteArrayList;
- import java.util.concurrent.ScheduledFuture;
- import java.util.concurrent.TimeUnit;
- import java.util.logging.Level;
- import java.util.logging.Logger;
- import com.l2jserver.commons.database.pool.impl.ConnectionFactory;
- import com.l2jserver.gameserver.ThreadPoolManager;
- import com.l2jserver.gameserver.datatables.SpawnTable;
- import com.l2jserver.gameserver.idfactory.IdFactory;
- import com.l2jserver.gameserver.instancemanager.MapRegionManager;
- import com.l2jserver.gameserver.model.actor.L2Npc;
- import com.l2jserver.gameserver.model.interfaces.IIdentifiable;
- import com.l2jserver.gameserver.util.Broadcast;
- import com.l2jserver.util.Rnd;
- /**
- * Auto Spawn handler.<br>
- * Allows spawning of a NPC object based on a timer.<br>
- * (From the official idea used for the Merchant and Blacksmith of Mammon)<br>
- * General Usage: - Call registerSpawn() with the parameters listed below.<br>
- * int npcId int[][] spawnPoints or specify NULL to add points later.<br>
- * int initialDelay (If < 0 = default value) int respawnDelay (If < 0 = default value)<br>
- * int despawnDelay (If < 0 = default value or if = 0, function disabled)<br>
- * spawnPoints is a standard two-dimensional int array containing X,Y and Z coordinates.<br>
- * The default respawn/despawn delays are currently every hour (as for Mammon on official servers).<br>
- * The resulting AutoSpawnInstance object represents the newly added spawn index.<br>
- * The internal methods of this object can be used to adjust random spawning, for instance a call to setRandomSpawn(1, true); would set the spawn at index 1 to be randomly rather than sequentially-based.<br>
- * Also they can be used to specify the number of NPC instances to spawn using setSpawnCount(), and broadcast a message to all users using setBroadcast().<br>
- * Random Spawning = OFF by default Broadcasting = OFF by default
- * @author Tempy
- */
- public class AutoSpawnHandler
- {
- protected static final Logger _log = Logger.getLogger(AutoSpawnHandler.class.getName());
-
- private static final int DEFAULT_INITIAL_SPAWN = 30000; // 30 seconds after registration
- private static final int DEFAULT_RESPAWN = 3600000; // 1 hour in millisecs
- private static final int DEFAULT_DESPAWN = 3600000; // 1 hour in millisecs
-
- protected Map<Integer, AutoSpawnInstance> _registeredSpawns = new ConcurrentHashMap<>();
- protected Map<Integer, ScheduledFuture<?>> _runningSpawns = new ConcurrentHashMap<>();
-
- protected boolean _activeState = true;
-
- protected AutoSpawnHandler()
- {
- restoreSpawnData();
- }
-
- public static AutoSpawnHandler getInstance()
- {
- return SingletonHolder._instance;
- }
-
- public final int size()
- {
- return _registeredSpawns.size();
- }
-
- public void reload()
- {
- // stop all timers
- for (ScheduledFuture<?> sf : _runningSpawns.values())
- {
- if (sf != null)
- {
- sf.cancel(true);
- }
- }
- // unregister all registered spawns
- for (AutoSpawnInstance asi : _registeredSpawns.values())
- {
- if (asi != null)
- {
- this.removeSpawn(asi);
- }
- }
-
- // create clean list
- _registeredSpawns.clear();
- _runningSpawns.clear();
-
- // load
- restoreSpawnData();
- }
-
- private void restoreSpawnData()
- {
- try (Connection con = ConnectionFactory.getInstance().getConnection();
- Statement s = con.createStatement();
- ResultSet rs = s.executeQuery("SELECT * FROM random_spawn ORDER BY groupId ASC");
- PreparedStatement ps = con.prepareStatement("SELECT * FROM random_spawn_loc WHERE groupId=?"))
- {
- // Restore spawn group data, then the location data.
- while (rs.next())
- {
- // Register random spawn group, set various options on the
- // created spawn instance.
- AutoSpawnInstance spawnInst = registerSpawn(rs.getInt("npcId"), rs.getInt("initialDelay"), rs.getInt("respawnDelay"), rs.getInt("despawnDelay"));
-
- spawnInst.setSpawnCount(rs.getInt("count"));
- spawnInst.setBroadcast(rs.getBoolean("broadcastSpawn"));
- spawnInst.setRandomSpawn(rs.getBoolean("randomSpawn"));
-
- // Restore the spawn locations for this spawn group/instance.
- ps.setInt(1, rs.getInt("groupId"));
- try (ResultSet rs2 = ps.executeQuery())
- {
- ps.clearParameters();
-
- while (rs2.next())
- {
- // Add each location to the spawn group/instance.
- spawnInst.addSpawnLocation(rs2.getInt("x"), rs2.getInt("y"), rs2.getInt("z"), rs2.getInt("heading"));
- }
- }
- }
- }
- catch (Exception e)
- {
- _log.log(Level.WARNING, "AutoSpawnHandler: Could not restore spawn data: " + e.getMessage(), e);
- }
- }
-
- /**
- * Registers a spawn with the given parameters with the spawner, and marks it as active.<br>
- * Returns a AutoSpawnInstance containing info about the spawn.
- * @param npcId
- * @param spawnPoints
- * @param initialDelay (If < 0 = default value)
- * @param respawnDelay (If < 0 = default value)
- * @param despawnDelay (If < 0 = default value or if = 0, function disabled)
- * @return AutoSpawnInstance spawnInst
- */
- public AutoSpawnInstance registerSpawn(int npcId, int[][] spawnPoints, int initialDelay, int respawnDelay, int despawnDelay)
- {
- if (initialDelay < 0)
- {
- initialDelay = DEFAULT_INITIAL_SPAWN;
- }
-
- if (respawnDelay < 0)
- {
- respawnDelay = DEFAULT_RESPAWN;
- }
-
- if (despawnDelay < 0)
- {
- despawnDelay = DEFAULT_DESPAWN;
- }
-
- AutoSpawnInstance newSpawn = new AutoSpawnInstance(npcId, initialDelay, respawnDelay, despawnDelay);
-
- if (spawnPoints != null)
- {
- for (int[] spawnPoint : spawnPoints)
- {
- newSpawn.addSpawnLocation(spawnPoint);
- }
- }
-
- int newId = IdFactory.getInstance().getNextId();
- newSpawn._objectId = newId;
- _registeredSpawns.put(newId, newSpawn);
-
- setSpawnActive(newSpawn, true);
- return newSpawn;
- }
-
- /**
- * Registers a spawn with the given parameters with the spawner, and marks it as active.<br>
- * Returns a AutoSpawnInstance containing info about the spawn.<br>
- * <B>Warning:</B> Spawn locations must be specified separately using addSpawnLocation().
- * @param npcId
- * @param initialDelay (If < 0 = default value)
- * @param respawnDelay (If < 0 = default value)
- * @param despawnDelay (If < 0 = default value or if = 0, function disabled)
- * @return AutoSpawnInstance spawnInst
- */
- public AutoSpawnInstance registerSpawn(int npcId, int initialDelay, int respawnDelay, int despawnDelay)
- {
- return registerSpawn(npcId, null, initialDelay, respawnDelay, despawnDelay);
- }
-
- /**
- * Remove a registered spawn from the list, specified by the given spawn instance.
- * @param spawnInst
- * @return boolean removedSuccessfully
- */
- public boolean removeSpawn(AutoSpawnInstance spawnInst)
- {
- if (!isSpawnRegistered(spawnInst))
- {
- return false;
- }
-
- try
- {
- // Try to remove from the list of registered spawns if it exists.
- _registeredSpawns.remove(spawnInst.getId());
-
- // Cancel the currently associated running scheduled task.
- ScheduledFuture<?> respawnTask = _runningSpawns.remove(spawnInst._objectId);
- respawnTask.cancel(false);
- }
- catch (Exception e)
- {
- _log.log(Level.WARNING, "AutoSpawnHandler: Could not auto spawn for NPC ID " + spawnInst._npcId + " (Object ID = " + spawnInst._objectId + "): " + e.getMessage(), e);
- return false;
- }
-
- return true;
- }
-
- /**
- * Remove a registered spawn from the list, specified by the given spawn object ID.
- * @param objectId
- */
- public void removeSpawn(int objectId)
- {
- removeSpawn(_registeredSpawns.get(objectId));
- }
-
- /**
- * Sets the active state of the specified spawn.
- * @param spawnInst
- * @param isActive
- */
- public void setSpawnActive(AutoSpawnInstance spawnInst, boolean isActive)
- {
- if (spawnInst == null)
- {
- return;
- }
-
- int objectId = spawnInst._objectId;
-
- if (isSpawnRegistered(objectId))
- {
- ScheduledFuture<?> spawnTask = null;
-
- if (isActive)
- {
- AutoSpawner rs = new AutoSpawner(objectId);
-
- if (spawnInst._desDelay > 0)
- {
- spawnTask = ThreadPoolManager.getInstance().scheduleEffectAtFixedRate(rs, spawnInst._initDelay, spawnInst._resDelay);
- }
- else
- {
- spawnTask = ThreadPoolManager.getInstance().scheduleEffect(rs, spawnInst._initDelay);
- }
-
- _runningSpawns.put(objectId, spawnTask);
- }
- else
- {
- AutoDespawner rd = new AutoDespawner(objectId);
- spawnTask = _runningSpawns.remove(objectId);
-
- if (spawnTask != null)
- {
- spawnTask.cancel(false);
- }
-
- ThreadPoolManager.getInstance().scheduleEffect(rd, 0);
- }
-
- spawnInst.setSpawnActive(isActive);
- }
- }
-
- /**
- * Sets the active state of all auto spawn instances to that specified, and cancels the scheduled spawn task if necessary.
- * @param isActive
- */
- public void setAllActive(boolean isActive)
- {
- if (_activeState == isActive)
- {
- return;
- }
-
- for (AutoSpawnInstance spawnInst : _registeredSpawns.values())
- {
- setSpawnActive(spawnInst, isActive);
- }
-
- _activeState = isActive;
- }
-
- /**
- * Returns the number of milliseconds until the next occurrence of the given spawn.
- * @param spawnInst
- * @return
- */
- public final long getTimeToNextSpawn(AutoSpawnInstance spawnInst)
- {
- int objectId = spawnInst.getObjectId();
-
- if (!isSpawnRegistered(objectId))
- {
- return -1;
- }
-
- return (_runningSpawns.containsKey(objectId)) ? _runningSpawns.get(objectId).getDelay(TimeUnit.MILLISECONDS) : 0;
- }
-
- /**
- * Attempts to return the AutoSpawnInstance associated with the given NPC or Object ID type.<br>
- * Note: If isObjectId == false, returns first instance for the specified NPC ID.
- * @param id
- * @param isObjectId
- * @return AutoSpawnInstance spawnInst
- */
- public final AutoSpawnInstance getAutoSpawnInstance(int id, boolean isObjectId)
- {
- if (isObjectId)
- {
- if (isSpawnRegistered(id))
- {
- return _registeredSpawns.get(id);
- }
- }
- else
- {
- for (AutoSpawnInstance spawnInst : _registeredSpawns.values())
- {
- if (spawnInst.getId() == id)
- {
- return spawnInst;
- }
- }
- }
- return null;
- }
-
- public List<AutoSpawnInstance> getAutoSpawnInstances(int npcId)
- {
- final List<AutoSpawnInstance> result = new LinkedList<>();
- for (AutoSpawnInstance spawnInst : _registeredSpawns.values())
- {
- if (spawnInst.getId() == npcId)
- {
- result.add(spawnInst);
- }
- }
- return result;
- }
-
- /**
- * Tests if the specified object ID is assigned to an auto spawn.
- * @param objectId
- * @return isAssigned
- */
- public final boolean isSpawnRegistered(int objectId)
- {
- return _registeredSpawns.containsKey(objectId);
- }
-
- /**
- * Tests if the specified spawn instance is assigned to an auto spawn.
- * @param spawnInst
- * @return boolean isAssigned
- */
- public final boolean isSpawnRegistered(AutoSpawnInstance spawnInst)
- {
- return _registeredSpawns.containsValue(spawnInst);
- }
-
- /**
- * AutoSpawner class<br>
- * This handles the main spawn task for an auto spawn instance, and initializes a despawner if required.
- * @author Tempy
- */
- private class AutoSpawner implements Runnable
- {
- private final int _objectId;
-
- protected AutoSpawner(int objectId)
- {
- _objectId = objectId;
- }
-
- @Override
- public void run()
- {
- try
- {
- // Retrieve the required spawn instance for this spawn task.
- AutoSpawnInstance spawnInst = _registeredSpawns.get(_objectId);
-
- // If the spawn is not scheduled to be active, cancel the spawn
- // task.
- if (!spawnInst.isSpawnActive())
- {
- return;
- }
-
- Location[] locationList = spawnInst.getLocationList();
-
- // If there are no set co-ordinates, cancel the spawn task.
- if (locationList.length == 0)
- {
- _log.info("AutoSpawnHandler: No location co-ords specified for spawn instance (Object ID = " + _objectId + ").");
- return;
- }
-
- int locationCount = locationList.length;
- int locationIndex = Rnd.nextInt(locationCount);
-
- // If random spawning is disabled, the spawn at the next set of co-ordinates after the last.
- // If the index is greater than the number of possible spawns, reset the counter to zero.
- if (!spawnInst.isRandomSpawn())
- {
- locationIndex = spawnInst._lastLocIndex + 1;
-
- if (locationIndex == locationCount)
- {
- locationIndex = 0;
- }
-
- spawnInst._lastLocIndex = locationIndex;
- }
-
- // Set the X, Y and Z co-ordinates, where this spawn will take place.
- final int x = locationList[locationIndex].getX();
- final int y = locationList[locationIndex].getY();
- final int z = locationList[locationIndex].getZ();
- final int heading = locationList[locationIndex].getHeading();
-
- final L2Spawn newSpawn = new L2Spawn(spawnInst.getId());
- newSpawn.setX(x);
- newSpawn.setY(y);
- newSpawn.setZ(z);
- if (heading != -1)
- {
- newSpawn.setHeading(heading);
- }
- newSpawn.setAmount(spawnInst.getSpawnCount());
- if (spawnInst._desDelay == 0)
- {
- newSpawn.setRespawnDelay(spawnInst._resDelay);
- }
-
- // Add the new spawn information to the spawn table, but do not store it.
- SpawnTable.getInstance().addNewSpawn(newSpawn, false);
- L2Npc npcInst = null;
-
- if (spawnInst._spawnCount == 1)
- {
- npcInst = newSpawn.doSpawn();
- npcInst.setXYZ(npcInst.getX(), npcInst.getY(), npcInst.getZ());
- spawnInst.addNpcInstance(npcInst);
- }
- else
- {
- for (int i = 0; i < spawnInst._spawnCount; i++)
- {
- npcInst = newSpawn.doSpawn();
-
- // To prevent spawning of more than one NPC in the exact same spot, move it slightly by a small random offset.
- npcInst.setXYZ(npcInst.getX() + Rnd.nextInt(50), npcInst.getY() + Rnd.nextInt(50), npcInst.getZ());
-
- // Add the NPC instance to the list of managed instances.
- spawnInst.addNpcInstance(npcInst);
- }
- }
-
- if (npcInst != null)
- {
- String nearestTown = MapRegionManager.getInstance().getClosestTownName(npcInst);
-
- // Announce to all players that the spawn has taken place, with the nearest town location.
- if (spawnInst.isBroadcasting())
- {
- Broadcast.toAllOnlinePlayers("The " + npcInst.getName() + " has spawned near " + nearestTown + "!");
- }
- }
-
- // If there is no despawn time, do not create a despawn task.
- if (spawnInst.getDespawnDelay() > 0)
- {
- AutoDespawner rd = new AutoDespawner(_objectId);
- ThreadPoolManager.getInstance().scheduleAi(rd, spawnInst.getDespawnDelay() - 1000);
- }
- }
- catch (Exception e)
- {
- _log.log(Level.WARNING, "AutoSpawnHandler: An error occurred while initializing spawn instance (Object ID = " + _objectId + "): " + e.getMessage(), e);
- }
- }
- }
-
- /**
- * AutoDespawner Class<br>
- * Simply used as a secondary class for despawning an auto spawn instance.
- * @author Tempy
- */
- private class AutoDespawner implements Runnable
- {
- private final int _objectId;
-
- protected AutoDespawner(int objectId)
- {
- _objectId = objectId;
- }
-
- @Override
- public void run()
- {
- try
- {
- AutoSpawnInstance spawnInst = _registeredSpawns.get(_objectId);
-
- if (spawnInst == null)
- {
- _log.info("AutoSpawnHandler: No spawn registered for object ID = " + _objectId + ".");
- return;
- }
-
- for (L2Npc npcInst : spawnInst.getNPCInstanceList())
- {
- npcInst.deleteMe();
- SpawnTable.getInstance().deleteSpawn(npcInst.getSpawn(), false);
- spawnInst.removeNpcInstance(npcInst);
- }
- }
- catch (Exception e)
- {
- _log.log(Level.WARNING, "AutoSpawnHandler: An error occurred while despawning spawn (Object ID = " + _objectId + "): " + e.getMessage(), e);
- }
- }
- }
-
- /**
- * AutoSpawnInstance Class<br>
- * Stores information about a registered auto spawn.
- * @author Tempy
- */
- public static class AutoSpawnInstance implements IIdentifiable
- {
- protected int _objectId;
-
- protected int _spawnIndex;
-
- protected int _npcId;
-
- protected int _initDelay;
-
- protected int _resDelay;
-
- protected int _desDelay;
-
- protected int _spawnCount = 1;
-
- protected int _lastLocIndex = -1;
-
- private final Queue<L2Npc> _npcList = new ConcurrentLinkedQueue<>();
-
- private final List<Location> _locList = new CopyOnWriteArrayList<>();
-
- private boolean _spawnActive;
-
- private boolean _randomSpawn = false;
-
- private boolean _broadcastAnnouncement = false;
-
- protected AutoSpawnInstance(int npcId, int initDelay, int respawnDelay, int despawnDelay)
- {
- _npcId = npcId;
- _initDelay = initDelay;
- _resDelay = respawnDelay;
- _desDelay = despawnDelay;
- }
-
- protected void setSpawnActive(boolean activeValue)
- {
- _spawnActive = activeValue;
- }
-
- protected boolean addNpcInstance(L2Npc npcInst)
- {
- return _npcList.add(npcInst);
- }
-
- protected boolean removeNpcInstance(L2Npc npcInst)
- {
- return _npcList.remove(npcInst);
- }
-
- public int getObjectId()
- {
- return _objectId;
- }
-
- public int getInitialDelay()
- {
- return _initDelay;
- }
-
- public int getRespawnDelay()
- {
- return _resDelay;
- }
-
- public int getDespawnDelay()
- {
- return _desDelay;
- }
-
- /**
- * Gets the NPC ID.
- * @return the NPC ID
- */
- @Override
- public int getId()
- {
- return _npcId;
- }
-
- public int getSpawnCount()
- {
- return _spawnCount;
- }
-
- public Location[] getLocationList()
- {
- return _locList.toArray(new Location[_locList.size()]);
- }
-
- public Queue<L2Npc> getNPCInstanceList()
- {
- return _npcList;
- }
-
- public List<L2Spawn> getSpawns()
- {
- final List<L2Spawn> npcSpawns = new ArrayList<>();
- for (L2Npc npcInst : _npcList)
- {
- npcSpawns.add(npcInst.getSpawn());
- }
- return npcSpawns;
- }
-
- public void setSpawnCount(int spawnCount)
- {
- _spawnCount = spawnCount;
- }
-
- public void setRandomSpawn(boolean randValue)
- {
- _randomSpawn = randValue;
- }
-
- public void setBroadcast(boolean broadcastValue)
- {
- _broadcastAnnouncement = broadcastValue;
- }
-
- public boolean isSpawnActive()
- {
- return _spawnActive;
- }
-
- public boolean isRandomSpawn()
- {
- return _randomSpawn;
- }
-
- public boolean isBroadcasting()
- {
- return _broadcastAnnouncement;
- }
-
- public boolean addSpawnLocation(int x, int y, int z, int heading)
- {
- return _locList.add(new Location(x, y, z, heading));
- }
-
- public boolean addSpawnLocation(int[] spawnLoc)
- {
- if (spawnLoc.length != 3)
- {
- return false;
- }
-
- return addSpawnLocation(spawnLoc[0], spawnLoc[1], spawnLoc[2], -1);
- }
-
- public Location removeSpawnLocation(int locIndex)
- {
- try
- {
- return _locList.remove(locIndex);
- }
- catch (IndexOutOfBoundsException e)
- {
- return null;
- }
- }
- }
-
- private static class SingletonHolder
- {
- protected static final AutoSpawnHandler _instance = new AutoSpawnHandler();
- }
- }
|