Proper handling for Quest timers concurrency.

This commit is contained in:
MobiusDevelopment 2020-01-30 11:59:53 +00:00
parent 25d3220ce2
commit 99c209f0ba
18 changed files with 1240 additions and 1547 deletions

View File

@ -23,13 +23,12 @@ import java.sql.ResultSet;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -87,10 +86,7 @@ public class Quest extends AbstractScript implements IIdentifiable
public static final Logger LOGGER = Logger.getLogger(Quest.class.getName()); public static final Logger LOGGER = Logger.getLogger(Quest.class.getName());
/** Map containing lists of timers from the name of the timer. */ /** Map containing lists of timers from the name of the timer. */
private Map<String, List<QuestTimer>> _questTimers = null; private final Map<String, List<QuestTimer>> _questTimers = new HashMap<>();
private final ReentrantReadWriteLock _rwLock = new ReentrantReadWriteLock();
private final WriteLock _writeLock = _rwLock.writeLock();
private final ReadLock _readLock = _rwLock.readLock();
/** Map containing all the start conditions. */ /** Map containing all the start conditions. */
private Set<QuestCondition> _startCondition = null; private Set<QuestCondition> _startCondition = null;
@ -252,7 +248,7 @@ public class Quest extends AbstractScript implements IIdentifiable
* Add a timer to the quest (if it doesn't exist already) and start it. * Add a timer to the quest (if it doesn't exist already) and start it.
* @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)}) * @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)})
* @param time time in ms for when to fire the timer * @param time time in ms for when to fire the timer
* @param npc the npc associated with this timer (can be null) * @param npc the NPC associated with this timer (can be null)
* @param player the player associated with this timer (can be null) * @param player the player associated with this timer (can be null)
* @see #startQuestTimer(String, long, Npc, PlayerInstance, boolean) * @see #startQuestTimer(String, long, Npc, PlayerInstance, boolean)
*/ */
@ -267,16 +263,6 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public Map<String, List<QuestTimer>> getQuestTimers() public Map<String, List<QuestTimer>> getQuestTimers()
{ {
if (_questTimers == null)
{
synchronized (this)
{
if (_questTimers == null)
{
_questTimers = new ConcurrentHashMap<>(1);
}
}
}
return _questTimers; return _questTimers;
} }
@ -284,26 +270,21 @@ public class Quest extends AbstractScript implements IIdentifiable
* Add a timer to the quest (if it doesn't exist already) and start it. * Add a timer to the quest (if it doesn't exist already) and start it.
* @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)}) * @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)})
* @param time time in ms for when to fire the timer * @param time time in ms for when to fire the timer
* @param npc the npc associated with this timer (can be null) * @param npc the NPC associated with this timer (can be null)
* @param player the player associated with this timer (can be null) * @param player the player associated with this timer (can be null)
* @param repeating indicates whether the timer is repeatable or one-time.<br> * @param repeating indicates whether the timer is repeatable or one-time.<br>
* If {@code true}, the task is repeated every {@code time} milliseconds until explicitly stopped. * If {@code true}, the task is repeated every {@code time} milliseconds until explicitly stopped.
*/ */
public void startQuestTimer(String name, long time, Npc npc, PlayerInstance player, boolean repeating) public void startQuestTimer(String name, long time, Npc npc, PlayerInstance player, boolean repeating)
{ {
final List<QuestTimer> timers = getQuestTimers().computeIfAbsent(name, k -> new ArrayList<>(1)); synchronized (_questTimers)
// If there exists a timer with this name, allow the timer only if the [npc, player] set is unique nulls act as wildcards.
if (getQuestTimer(name, npc, player) == null)
{ {
_writeLock.lock(); final List<QuestTimer> timers = _questTimers.computeIfAbsent(name, k -> new CopyOnWriteArrayList<>());
try // If there exists a timer with this name, allow the timer only if the [npc, player] set is unique nulls act as wildcards.
if (getQuestTimer(name, npc, player) == null)
{ {
timers.add(new QuestTimer(this, name, time, npc, player, repeating)); timers.add(new QuestTimer(this, name, time, npc, player, repeating));
} }
finally
{
_writeLock.unlock();
}
} }
} }
@ -316,31 +297,28 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public QuestTimer getQuestTimer(String name, Npc npc, PlayerInstance player) public QuestTimer getQuestTimer(String name, Npc npc, PlayerInstance player)
{ {
if (_questTimers == null) if (name == null)
{ {
return null; return null;
} }
final List<QuestTimer> timers = getQuestTimers().get(name); synchronized (_questTimers)
if (timers != null)
{ {
_readLock.lock(); final List<QuestTimer> timers = _questTimers.get(name);
try if ((timers == null) || timers.isEmpty())
{ {
for (QuestTimer timer : timers) return null;
}
for (QuestTimer timer : timers)
{
if ((timer != null) && timer.equals(this, name, npc, player))
{ {
if ((timer != null) && timer.equals(this, name, npc, player)) return timer;
{
return timer;
}
} }
} }
finally return null;
{
_readLock.unlock();
}
} }
return null;
} }
/** /**
@ -349,30 +327,27 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void cancelQuestTimers(String name) public void cancelQuestTimers(String name)
{ {
if (_questTimers == null) if (name == null)
{ {
return; return;
} }
final List<QuestTimer> timers = getQuestTimers().get(name); synchronized (_questTimers)
if (timers != null)
{ {
_writeLock.lock(); final List<QuestTimer> timers = _questTimers.get(name);
try if ((timers == null) || timers.isEmpty())
{ {
for (QuestTimer timer : timers) return;
}
for (QuestTimer timer : timers)
{
if (timer != null)
{ {
if (timer != null) timer.cancel();
{
timer.cancel();
}
} }
timers.clear();
}
finally
{
_writeLock.unlock();
} }
timers.clear();
} }
} }
@ -384,10 +359,28 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void cancelQuestTimer(String name, Npc npc, PlayerInstance player) public void cancelQuestTimer(String name, Npc npc, PlayerInstance player)
{ {
final QuestTimer timer = getQuestTimer(name, npc, player); if (name == null)
if (timer != null)
{ {
timer.cancel(); return;
}
synchronized (_questTimers)
{
final List<QuestTimer> timers = _questTimers.get(name);
if ((timers == null) || timers.isEmpty())
{
return;
}
for (QuestTimer timer : timers)
{
if ((timer != null) && timer.equals(this, name, npc, player))
{
timer.cancel();
timers.remove(timer);
return;
}
}
} }
} }
@ -398,20 +391,17 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void removeQuestTimer(QuestTimer timer) public void removeQuestTimer(QuestTimer timer)
{ {
if ((timer != null) && (_questTimers != null)) if (timer == null)
{ {
final List<QuestTimer> timers = getQuestTimers().get(timer.toString()); return;
}
synchronized (_questTimers)
{
final List<QuestTimer> timers = _questTimers.get(timer.toString());
if (timers != null) if (timers != null)
{ {
_writeLock.lock(); timers.remove(timer);
try
{
timers.remove(timer);
}
finally
{
_writeLock.unlock();
}
} }
} }
} }
@ -2833,25 +2823,17 @@ public class Quest extends AbstractScript implements IIdentifiable
// cancel all pending timers before reloading. // cancel all pending timers before reloading.
// if timers ought to be restarted, the quest can take care of it // if timers ought to be restarted, the quest can take care of it
// with its code (example: save global data indicating what timer must be restarted). // with its code (example: save global data indicating what timer must be restarted).
if (_questTimers != null) synchronized (_questTimers)
{ {
for (List<QuestTimer> timers : getQuestTimers().values()) for (List<QuestTimer> timers : _questTimers.values())
{ {
_readLock.lock(); for (QuestTimer timer : timers)
try
{ {
for (QuestTimer timer : timers) timer.cancel();
{
timer.cancel();
}
}
finally
{
_readLock.unlock();
} }
timers.clear(); timers.clear();
} }
getQuestTimers().clear(); _questTimers.clear();
} }
if (removeFromList) if (removeFromList)

View File

@ -23,13 +23,12 @@ import java.sql.ResultSet;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -88,10 +87,7 @@ public class Quest extends AbstractScript implements IIdentifiable
public static final Logger LOGGER = Logger.getLogger(Quest.class.getName()); public static final Logger LOGGER = Logger.getLogger(Quest.class.getName());
/** Map containing lists of timers from the name of the timer. */ /** Map containing lists of timers from the name of the timer. */
private Map<String, List<QuestTimer>> _questTimers = null; private final Map<String, List<QuestTimer>> _questTimers = new HashMap<>();
private final ReentrantReadWriteLock _rwLock = new ReentrantReadWriteLock();
private final WriteLock _writeLock = _rwLock.writeLock();
private final ReadLock _readLock = _rwLock.readLock();
/** Map containing all the start conditions. */ /** Map containing all the start conditions. */
private Set<QuestCondition> _startCondition = null; private Set<QuestCondition> _startCondition = null;
@ -254,7 +250,7 @@ public class Quest extends AbstractScript implements IIdentifiable
* Add a timer to the quest (if it doesn't exist already) and start it. * Add a timer to the quest (if it doesn't exist already) and start it.
* @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)}) * @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)})
* @param time time in ms for when to fire the timer * @param time time in ms for when to fire the timer
* @param npc the npc associated with this timer (can be null) * @param npc the NPC associated with this timer (can be null)
* @param player the player associated with this timer (can be null) * @param player the player associated with this timer (can be null)
* @see #startQuestTimer(String, long, Npc, PlayerInstance, boolean) * @see #startQuestTimer(String, long, Npc, PlayerInstance, boolean)
*/ */
@ -269,16 +265,6 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public Map<String, List<QuestTimer>> getQuestTimers() public Map<String, List<QuestTimer>> getQuestTimers()
{ {
if (_questTimers == null)
{
synchronized (this)
{
if (_questTimers == null)
{
_questTimers = new ConcurrentHashMap<>(1);
}
}
}
return _questTimers; return _questTimers;
} }
@ -286,26 +272,21 @@ public class Quest extends AbstractScript implements IIdentifiable
* Add a timer to the quest (if it doesn't exist already) and start it. * Add a timer to the quest (if it doesn't exist already) and start it.
* @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)}) * @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)})
* @param time time in ms for when to fire the timer * @param time time in ms for when to fire the timer
* @param npc the npc associated with this timer (can be null) * @param npc the NPC associated with this timer (can be null)
* @param player the player associated with this timer (can be null) * @param player the player associated with this timer (can be null)
* @param repeating indicates whether the timer is repeatable or one-time.<br> * @param repeating indicates whether the timer is repeatable or one-time.<br>
* If {@code true}, the task is repeated every {@code time} milliseconds until explicitly stopped. * If {@code true}, the task is repeated every {@code time} milliseconds until explicitly stopped.
*/ */
public void startQuestTimer(String name, long time, Npc npc, PlayerInstance player, boolean repeating) public void startQuestTimer(String name, long time, Npc npc, PlayerInstance player, boolean repeating)
{ {
final List<QuestTimer> timers = getQuestTimers().computeIfAbsent(name, k -> new ArrayList<>(1)); synchronized (_questTimers)
// If there exists a timer with this name, allow the timer only if the [npc, player] set is unique nulls act as wildcards.
if (getQuestTimer(name, npc, player) == null)
{ {
_writeLock.lock(); final List<QuestTimer> timers = _questTimers.computeIfAbsent(name, k -> new CopyOnWriteArrayList<>());
try // If there exists a timer with this name, allow the timer only if the [npc, player] set is unique nulls act as wildcards.
if (getQuestTimer(name, npc, player) == null)
{ {
timers.add(new QuestTimer(this, name, time, npc, player, repeating)); timers.add(new QuestTimer(this, name, time, npc, player, repeating));
} }
finally
{
_writeLock.unlock();
}
} }
} }
@ -318,31 +299,28 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public QuestTimer getQuestTimer(String name, Npc npc, PlayerInstance player) public QuestTimer getQuestTimer(String name, Npc npc, PlayerInstance player)
{ {
if (_questTimers == null) if (name == null)
{ {
return null; return null;
} }
final List<QuestTimer> timers = getQuestTimers().get(name); synchronized (_questTimers)
if (timers != null)
{ {
_readLock.lock(); final List<QuestTimer> timers = _questTimers.get(name);
try if ((timers == null) || timers.isEmpty())
{ {
for (QuestTimer timer : timers) return null;
}
for (QuestTimer timer : timers)
{
if ((timer != null) && timer.equals(this, name, npc, player))
{ {
if ((timer != null) && timer.equals(this, name, npc, player)) return timer;
{
return timer;
}
} }
} }
finally return null;
{
_readLock.unlock();
}
} }
return null;
} }
/** /**
@ -351,30 +329,27 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void cancelQuestTimers(String name) public void cancelQuestTimers(String name)
{ {
if (_questTimers == null) if (name == null)
{ {
return; return;
} }
final List<QuestTimer> timers = getQuestTimers().get(name); synchronized (_questTimers)
if (timers != null)
{ {
_writeLock.lock(); final List<QuestTimer> timers = _questTimers.get(name);
try if ((timers == null) || timers.isEmpty())
{ {
for (QuestTimer timer : timers) return;
}
for (QuestTimer timer : timers)
{
if (timer != null)
{ {
if (timer != null) timer.cancel();
{
timer.cancel();
}
} }
timers.clear();
}
finally
{
_writeLock.unlock();
} }
timers.clear();
} }
} }
@ -386,10 +361,28 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void cancelQuestTimer(String name, Npc npc, PlayerInstance player) public void cancelQuestTimer(String name, Npc npc, PlayerInstance player)
{ {
final QuestTimer timer = getQuestTimer(name, npc, player); if (name == null)
if (timer != null)
{ {
timer.cancel(); return;
}
synchronized (_questTimers)
{
final List<QuestTimer> timers = _questTimers.get(name);
if ((timers == null) || timers.isEmpty())
{
return;
}
for (QuestTimer timer : timers)
{
if ((timer != null) && timer.equals(this, name, npc, player))
{
timer.cancel();
timers.remove(timer);
return;
}
}
} }
} }
@ -400,20 +393,17 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void removeQuestTimer(QuestTimer timer) public void removeQuestTimer(QuestTimer timer)
{ {
if ((timer != null) && (_questTimers != null)) if (timer == null)
{ {
final List<QuestTimer> timers = getQuestTimers().get(timer.toString()); return;
}
synchronized (_questTimers)
{
final List<QuestTimer> timers = _questTimers.get(timer.toString());
if (timers != null) if (timers != null)
{ {
_writeLock.lock(); timers.remove(timer);
try
{
timers.remove(timer);
}
finally
{
_writeLock.unlock();
}
} }
} }
} }
@ -2835,25 +2825,17 @@ public class Quest extends AbstractScript implements IIdentifiable
// cancel all pending timers before reloading. // cancel all pending timers before reloading.
// if timers ought to be restarted, the quest can take care of it // if timers ought to be restarted, the quest can take care of it
// with its code (example: save global data indicating what timer must be restarted). // with its code (example: save global data indicating what timer must be restarted).
if (_questTimers != null) synchronized (_questTimers)
{ {
for (List<QuestTimer> timers : getQuestTimers().values()) for (List<QuestTimer> timers : _questTimers.values())
{ {
_readLock.lock(); for (QuestTimer timer : timers)
try
{ {
for (QuestTimer timer : timers) timer.cancel();
{
timer.cancel();
}
}
finally
{
_readLock.unlock();
} }
timers.clear(); timers.clear();
} }
getQuestTimers().clear(); _questTimers.clear();
} }
if (removeFromList) if (removeFromList)

View File

@ -23,13 +23,12 @@ import java.sql.ResultSet;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -89,10 +88,7 @@ public class Quest extends AbstractScript implements IIdentifiable
public static final Logger LOGGER = Logger.getLogger(Quest.class.getName()); public static final Logger LOGGER = Logger.getLogger(Quest.class.getName());
/** Map containing lists of timers from the name of the timer. */ /** Map containing lists of timers from the name of the timer. */
private Map<String, List<QuestTimer>> _questTimers = null; private final Map<String, List<QuestTimer>> _questTimers = new HashMap<>();
private final ReentrantReadWriteLock _rwLock = new ReentrantReadWriteLock();
private final WriteLock _writeLock = _rwLock.writeLock();
private final ReadLock _readLock = _rwLock.readLock();
/** Map containing all the start conditions. */ /** Map containing all the start conditions. */
private Set<QuestCondition> _startCondition = null; private Set<QuestCondition> _startCondition = null;
@ -255,7 +251,7 @@ public class Quest extends AbstractScript implements IIdentifiable
* Add a timer to the quest (if it doesn't exist already) and start it. * Add a timer to the quest (if it doesn't exist already) and start it.
* @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)}) * @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)})
* @param time time in ms for when to fire the timer * @param time time in ms for when to fire the timer
* @param npc the npc associated with this timer (can be null) * @param npc the NPC associated with this timer (can be null)
* @param player the player associated with this timer (can be null) * @param player the player associated with this timer (can be null)
* @see #startQuestTimer(String, long, Npc, PlayerInstance, boolean) * @see #startQuestTimer(String, long, Npc, PlayerInstance, boolean)
*/ */
@ -270,16 +266,6 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public Map<String, List<QuestTimer>> getQuestTimers() public Map<String, List<QuestTimer>> getQuestTimers()
{ {
if (_questTimers == null)
{
synchronized (this)
{
if (_questTimers == null)
{
_questTimers = new ConcurrentHashMap<>(1);
}
}
}
return _questTimers; return _questTimers;
} }
@ -287,26 +273,21 @@ public class Quest extends AbstractScript implements IIdentifiable
* Add a timer to the quest (if it doesn't exist already) and start it. * Add a timer to the quest (if it doesn't exist already) and start it.
* @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)}) * @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)})
* @param time time in ms for when to fire the timer * @param time time in ms for when to fire the timer
* @param npc the npc associated with this timer (can be null) * @param npc the NPC associated with this timer (can be null)
* @param player the player associated with this timer (can be null) * @param player the player associated with this timer (can be null)
* @param repeating indicates whether the timer is repeatable or one-time.<br> * @param repeating indicates whether the timer is repeatable or one-time.<br>
* If {@code true}, the task is repeated every {@code time} milliseconds until explicitly stopped. * If {@code true}, the task is repeated every {@code time} milliseconds until explicitly stopped.
*/ */
public void startQuestTimer(String name, long time, Npc npc, PlayerInstance player, boolean repeating) public void startQuestTimer(String name, long time, Npc npc, PlayerInstance player, boolean repeating)
{ {
final List<QuestTimer> timers = getQuestTimers().computeIfAbsent(name, k -> new ArrayList<>(1)); synchronized (_questTimers)
// If there exists a timer with this name, allow the timer only if the [npc, player] set is unique nulls act as wildcards.
if (getQuestTimer(name, npc, player) == null)
{ {
_writeLock.lock(); final List<QuestTimer> timers = _questTimers.computeIfAbsent(name, k -> new CopyOnWriteArrayList<>());
try // If there exists a timer with this name, allow the timer only if the [npc, player] set is unique nulls act as wildcards.
if (getQuestTimer(name, npc, player) == null)
{ {
timers.add(new QuestTimer(this, name, time, npc, player, repeating)); timers.add(new QuestTimer(this, name, time, npc, player, repeating));
} }
finally
{
_writeLock.unlock();
}
} }
} }
@ -319,31 +300,28 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public QuestTimer getQuestTimer(String name, Npc npc, PlayerInstance player) public QuestTimer getQuestTimer(String name, Npc npc, PlayerInstance player)
{ {
if (_questTimers == null) if (name == null)
{ {
return null; return null;
} }
final List<QuestTimer> timers = getQuestTimers().get(name); synchronized (_questTimers)
if (timers != null)
{ {
_readLock.lock(); final List<QuestTimer> timers = _questTimers.get(name);
try if ((timers == null) || timers.isEmpty())
{ {
for (QuestTimer timer : timers) return null;
}
for (QuestTimer timer : timers)
{
if ((timer != null) && timer.equals(this, name, npc, player))
{ {
if ((timer != null) && timer.equals(this, name, npc, player)) return timer;
{
return timer;
}
} }
} }
finally return null;
{
_readLock.unlock();
}
} }
return null;
} }
/** /**
@ -352,30 +330,27 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void cancelQuestTimers(String name) public void cancelQuestTimers(String name)
{ {
if (_questTimers == null) if (name == null)
{ {
return; return;
} }
final List<QuestTimer> timers = getQuestTimers().get(name); synchronized (_questTimers)
if (timers != null)
{ {
_writeLock.lock(); final List<QuestTimer> timers = _questTimers.get(name);
try if ((timers == null) || timers.isEmpty())
{ {
for (QuestTimer timer : timers) return;
}
for (QuestTimer timer : timers)
{
if (timer != null)
{ {
if (timer != null) timer.cancel();
{
timer.cancel();
}
} }
timers.clear();
}
finally
{
_writeLock.unlock();
} }
timers.clear();
} }
} }
@ -387,10 +362,28 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void cancelQuestTimer(String name, Npc npc, PlayerInstance player) public void cancelQuestTimer(String name, Npc npc, PlayerInstance player)
{ {
final QuestTimer timer = getQuestTimer(name, npc, player); if (name == null)
if (timer != null)
{ {
timer.cancel(); return;
}
synchronized (_questTimers)
{
final List<QuestTimer> timers = _questTimers.get(name);
if ((timers == null) || timers.isEmpty())
{
return;
}
for (QuestTimer timer : timers)
{
if ((timer != null) && timer.equals(this, name, npc, player))
{
timer.cancel();
timers.remove(timer);
return;
}
}
} }
} }
@ -401,20 +394,17 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void removeQuestTimer(QuestTimer timer) public void removeQuestTimer(QuestTimer timer)
{ {
if ((timer != null) && (_questTimers != null)) if (timer == null)
{ {
final List<QuestTimer> timers = getQuestTimers().get(timer.toString()); return;
}
synchronized (_questTimers)
{
final List<QuestTimer> timers = _questTimers.get(timer.toString());
if (timers != null) if (timers != null)
{ {
_writeLock.lock(); timers.remove(timer);
try
{
timers.remove(timer);
}
finally
{
_writeLock.unlock();
}
} }
} }
} }
@ -2836,25 +2826,17 @@ public class Quest extends AbstractScript implements IIdentifiable
// cancel all pending timers before reloading. // cancel all pending timers before reloading.
// if timers ought to be restarted, the quest can take care of it // if timers ought to be restarted, the quest can take care of it
// with its code (example: save global data indicating what timer must be restarted). // with its code (example: save global data indicating what timer must be restarted).
if (_questTimers != null) synchronized (_questTimers)
{ {
for (List<QuestTimer> timers : getQuestTimers().values()) for (List<QuestTimer> timers : _questTimers.values())
{ {
_readLock.lock(); for (QuestTimer timer : timers)
try
{ {
for (QuestTimer timer : timers) timer.cancel();
{
timer.cancel();
}
}
finally
{
_readLock.unlock();
} }
timers.clear(); timers.clear();
} }
getQuestTimers().clear(); _questTimers.clear();
} }
if (removeFromList) if (removeFromList)

View File

@ -23,13 +23,12 @@ import java.sql.ResultSet;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -90,10 +89,7 @@ public class Quest extends AbstractScript implements IIdentifiable
public static final Logger LOGGER = Logger.getLogger(Quest.class.getName()); public static final Logger LOGGER = Logger.getLogger(Quest.class.getName());
/** Map containing lists of timers from the name of the timer. */ /** Map containing lists of timers from the name of the timer. */
private Map<String, List<QuestTimer>> _questTimers = null; private final Map<String, List<QuestTimer>> _questTimers = new HashMap<>();
private final ReentrantReadWriteLock _rwLock = new ReentrantReadWriteLock();
private final WriteLock _writeLock = _rwLock.writeLock();
private final ReadLock _readLock = _rwLock.readLock();
/** Map containing all the start conditions. */ /** Map containing all the start conditions. */
private Set<QuestCondition> _startCondition = null; private Set<QuestCondition> _startCondition = null;
@ -255,7 +251,7 @@ public class Quest extends AbstractScript implements IIdentifiable
* Add a timer to the quest (if it doesn't exist already) and start it. * Add a timer to the quest (if it doesn't exist already) and start it.
* @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)}) * @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)})
* @param time time in ms for when to fire the timer * @param time time in ms for when to fire the timer
* @param npc the npc associated with this timer (can be null) * @param npc the NPC associated with this timer (can be null)
* @param player the player associated with this timer (can be null) * @param player the player associated with this timer (can be null)
* @see #startQuestTimer(String, long, Npc, PlayerInstance, boolean) * @see #startQuestTimer(String, long, Npc, PlayerInstance, boolean)
*/ */
@ -270,16 +266,6 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public Map<String, List<QuestTimer>> getQuestTimers() public Map<String, List<QuestTimer>> getQuestTimers()
{ {
if (_questTimers == null)
{
synchronized (this)
{
if (_questTimers == null)
{
_questTimers = new ConcurrentHashMap<>(1);
}
}
}
return _questTimers; return _questTimers;
} }
@ -287,26 +273,21 @@ public class Quest extends AbstractScript implements IIdentifiable
* Add a timer to the quest (if it doesn't exist already) and start it. * Add a timer to the quest (if it doesn't exist already) and start it.
* @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)}) * @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)})
* @param time time in ms for when to fire the timer * @param time time in ms for when to fire the timer
* @param npc the npc associated with this timer (can be null) * @param npc the NPC associated with this timer (can be null)
* @param player the player associated with this timer (can be null) * @param player the player associated with this timer (can be null)
* @param repeating indicates whether the timer is repeatable or one-time.<br> * @param repeating indicates whether the timer is repeatable or one-time.<br>
* If {@code true}, the task is repeated every {@code time} milliseconds until explicitly stopped. * If {@code true}, the task is repeated every {@code time} milliseconds until explicitly stopped.
*/ */
public void startQuestTimer(String name, long time, Npc npc, PlayerInstance player, boolean repeating) public void startQuestTimer(String name, long time, Npc npc, PlayerInstance player, boolean repeating)
{ {
final List<QuestTimer> timers = getQuestTimers().computeIfAbsent(name, k -> new ArrayList<>(1)); synchronized (_questTimers)
// If there exists a timer with this name, allow the timer only if the [npc, player] set is unique nulls act as wildcards.
if (getQuestTimer(name, npc, player) == null)
{ {
_writeLock.lock(); final List<QuestTimer> timers = _questTimers.computeIfAbsent(name, k -> new CopyOnWriteArrayList<>());
try // If there exists a timer with this name, allow the timer only if the [npc, player] set is unique nulls act as wildcards.
if (getQuestTimer(name, npc, player) == null)
{ {
timers.add(new QuestTimer(this, name, time, npc, player, repeating)); timers.add(new QuestTimer(this, name, time, npc, player, repeating));
} }
finally
{
_writeLock.unlock();
}
} }
} }
@ -319,31 +300,28 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public QuestTimer getQuestTimer(String name, Npc npc, PlayerInstance player) public QuestTimer getQuestTimer(String name, Npc npc, PlayerInstance player)
{ {
if (_questTimers == null) if (name == null)
{ {
return null; return null;
} }
final List<QuestTimer> timers = getQuestTimers().get(name); synchronized (_questTimers)
if (timers != null)
{ {
_readLock.lock(); final List<QuestTimer> timers = _questTimers.get(name);
try if ((timers == null) || timers.isEmpty())
{ {
for (QuestTimer timer : timers) return null;
}
for (QuestTimer timer : timers)
{
if ((timer != null) && timer.equals(this, name, npc, player))
{ {
if ((timer != null) && timer.equals(this, name, npc, player)) return timer;
{
return timer;
}
} }
} }
finally return null;
{
_readLock.unlock();
}
} }
return null;
} }
/** /**
@ -352,30 +330,27 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void cancelQuestTimers(String name) public void cancelQuestTimers(String name)
{ {
if (_questTimers == null) if (name == null)
{ {
return; return;
} }
final List<QuestTimer> timers = getQuestTimers().get(name); synchronized (_questTimers)
if (timers != null)
{ {
_writeLock.lock(); final List<QuestTimer> timers = _questTimers.get(name);
try if ((timers == null) || timers.isEmpty())
{ {
for (QuestTimer timer : timers) return;
}
for (QuestTimer timer : timers)
{
if (timer != null)
{ {
if (timer != null) timer.cancel();
{
timer.cancel();
}
} }
timers.clear();
}
finally
{
_writeLock.unlock();
} }
timers.clear();
} }
} }
@ -387,10 +362,28 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void cancelQuestTimer(String name, Npc npc, PlayerInstance player) public void cancelQuestTimer(String name, Npc npc, PlayerInstance player)
{ {
final QuestTimer timer = getQuestTimer(name, npc, player); if (name == null)
if (timer != null)
{ {
timer.cancel(); return;
}
synchronized (_questTimers)
{
final List<QuestTimer> timers = _questTimers.get(name);
if ((timers == null) || timers.isEmpty())
{
return;
}
for (QuestTimer timer : timers)
{
if ((timer != null) && timer.equals(this, name, npc, player))
{
timer.cancel();
timers.remove(timer);
return;
}
}
} }
} }
@ -401,20 +394,17 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void removeQuestTimer(QuestTimer timer) public void removeQuestTimer(QuestTimer timer)
{ {
if ((timer != null) && (_questTimers != null)) if (timer == null)
{ {
final List<QuestTimer> timers = getQuestTimers().get(timer.toString()); return;
}
synchronized (_questTimers)
{
final List<QuestTimer> timers = _questTimers.get(timer.toString());
if (timers != null) if (timers != null)
{ {
_writeLock.lock(); timers.remove(timer);
try
{
timers.remove(timer);
}
finally
{
_writeLock.unlock();
}
} }
} }
} }
@ -2836,25 +2826,17 @@ public class Quest extends AbstractScript implements IIdentifiable
// cancel all pending timers before reloading. // cancel all pending timers before reloading.
// if timers ought to be restarted, the quest can take care of it // if timers ought to be restarted, the quest can take care of it
// with its code (example: save global data indicating what timer must be restarted). // with its code (example: save global data indicating what timer must be restarted).
if (_questTimers != null) synchronized (_questTimers)
{ {
for (List<QuestTimer> timers : getQuestTimers().values()) for (List<QuestTimer> timers : _questTimers.values())
{ {
_readLock.lock(); for (QuestTimer timer : timers)
try
{ {
for (QuestTimer timer : timers) timer.cancel();
{
timer.cancel();
}
}
finally
{
_readLock.unlock();
} }
timers.clear(); timers.clear();
} }
getQuestTimers().clear(); _questTimers.clear();
} }
if (removeFromList) if (removeFromList)

View File

@ -23,13 +23,12 @@ import java.sql.ResultSet;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -90,10 +89,7 @@ public class Quest extends AbstractScript implements IIdentifiable
public static final Logger LOGGER = Logger.getLogger(Quest.class.getName()); public static final Logger LOGGER = Logger.getLogger(Quest.class.getName());
/** Map containing lists of timers from the name of the timer. */ /** Map containing lists of timers from the name of the timer. */
private Map<String, List<QuestTimer>> _questTimers = null; private final Map<String, List<QuestTimer>> _questTimers = new HashMap<>();
private final ReentrantReadWriteLock _rwLock = new ReentrantReadWriteLock();
private final WriteLock _writeLock = _rwLock.writeLock();
private final ReadLock _readLock = _rwLock.readLock();
/** Map containing all the start conditions. */ /** Map containing all the start conditions. */
private Set<QuestCondition> _startCondition = null; private Set<QuestCondition> _startCondition = null;
@ -255,7 +251,7 @@ public class Quest extends AbstractScript implements IIdentifiable
* Add a timer to the quest (if it doesn't exist already) and start it. * Add a timer to the quest (if it doesn't exist already) and start it.
* @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)}) * @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)})
* @param time time in ms for when to fire the timer * @param time time in ms for when to fire the timer
* @param npc the npc associated with this timer (can be null) * @param npc the NPC associated with this timer (can be null)
* @param player the player associated with this timer (can be null) * @param player the player associated with this timer (can be null)
* @see #startQuestTimer(String, long, Npc, PlayerInstance, boolean) * @see #startQuestTimer(String, long, Npc, PlayerInstance, boolean)
*/ */
@ -270,16 +266,6 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public Map<String, List<QuestTimer>> getQuestTimers() public Map<String, List<QuestTimer>> getQuestTimers()
{ {
if (_questTimers == null)
{
synchronized (this)
{
if (_questTimers == null)
{
_questTimers = new ConcurrentHashMap<>(1);
}
}
}
return _questTimers; return _questTimers;
} }
@ -287,26 +273,21 @@ public class Quest extends AbstractScript implements IIdentifiable
* Add a timer to the quest (if it doesn't exist already) and start it. * Add a timer to the quest (if it doesn't exist already) and start it.
* @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)}) * @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)})
* @param time time in ms for when to fire the timer * @param time time in ms for when to fire the timer
* @param npc the npc associated with this timer (can be null) * @param npc the NPC associated with this timer (can be null)
* @param player the player associated with this timer (can be null) * @param player the player associated with this timer (can be null)
* @param repeating indicates whether the timer is repeatable or one-time.<br> * @param repeating indicates whether the timer is repeatable or one-time.<br>
* If {@code true}, the task is repeated every {@code time} milliseconds until explicitly stopped. * If {@code true}, the task is repeated every {@code time} milliseconds until explicitly stopped.
*/ */
public void startQuestTimer(String name, long time, Npc npc, PlayerInstance player, boolean repeating) public void startQuestTimer(String name, long time, Npc npc, PlayerInstance player, boolean repeating)
{ {
final List<QuestTimer> timers = getQuestTimers().computeIfAbsent(name, k -> new ArrayList<>(1)); synchronized (_questTimers)
// If there exists a timer with this name, allow the timer only if the [npc, player] set is unique nulls act as wildcards.
if (getQuestTimer(name, npc, player) == null)
{ {
_writeLock.lock(); final List<QuestTimer> timers = _questTimers.computeIfAbsent(name, k -> new CopyOnWriteArrayList<>());
try // If there exists a timer with this name, allow the timer only if the [npc, player] set is unique nulls act as wildcards.
if (getQuestTimer(name, npc, player) == null)
{ {
timers.add(new QuestTimer(this, name, time, npc, player, repeating)); timers.add(new QuestTimer(this, name, time, npc, player, repeating));
} }
finally
{
_writeLock.unlock();
}
} }
} }
@ -319,31 +300,28 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public QuestTimer getQuestTimer(String name, Npc npc, PlayerInstance player) public QuestTimer getQuestTimer(String name, Npc npc, PlayerInstance player)
{ {
if (_questTimers == null) if (name == null)
{ {
return null; return null;
} }
final List<QuestTimer> timers = getQuestTimers().get(name); synchronized (_questTimers)
if (timers != null)
{ {
_readLock.lock(); final List<QuestTimer> timers = _questTimers.get(name);
try if ((timers == null) || timers.isEmpty())
{ {
for (QuestTimer timer : timers) return null;
}
for (QuestTimer timer : timers)
{
if ((timer != null) && timer.equals(this, name, npc, player))
{ {
if ((timer != null) && timer.equals(this, name, npc, player)) return timer;
{
return timer;
}
} }
} }
finally return null;
{
_readLock.unlock();
}
} }
return null;
} }
/** /**
@ -352,30 +330,27 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void cancelQuestTimers(String name) public void cancelQuestTimers(String name)
{ {
if (_questTimers == null) if (name == null)
{ {
return; return;
} }
final List<QuestTimer> timers = getQuestTimers().get(name); synchronized (_questTimers)
if (timers != null)
{ {
_writeLock.lock(); final List<QuestTimer> timers = _questTimers.get(name);
try if ((timers == null) || timers.isEmpty())
{ {
for (QuestTimer timer : timers) return;
}
for (QuestTimer timer : timers)
{
if (timer != null)
{ {
if (timer != null) timer.cancel();
{
timer.cancel();
}
} }
timers.clear();
}
finally
{
_writeLock.unlock();
} }
timers.clear();
} }
} }
@ -387,10 +362,28 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void cancelQuestTimer(String name, Npc npc, PlayerInstance player) public void cancelQuestTimer(String name, Npc npc, PlayerInstance player)
{ {
final QuestTimer timer = getQuestTimer(name, npc, player); if (name == null)
if (timer != null)
{ {
timer.cancel(); return;
}
synchronized (_questTimers)
{
final List<QuestTimer> timers = _questTimers.get(name);
if ((timers == null) || timers.isEmpty())
{
return;
}
for (QuestTimer timer : timers)
{
if ((timer != null) && timer.equals(this, name, npc, player))
{
timer.cancel();
timers.remove(timer);
return;
}
}
} }
} }
@ -401,20 +394,17 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void removeQuestTimer(QuestTimer timer) public void removeQuestTimer(QuestTimer timer)
{ {
if ((timer != null) && (_questTimers != null)) if (timer == null)
{ {
final List<QuestTimer> timers = getQuestTimers().get(timer.toString()); return;
}
synchronized (_questTimers)
{
final List<QuestTimer> timers = _questTimers.get(timer.toString());
if (timers != null) if (timers != null)
{ {
_writeLock.lock(); timers.remove(timer);
try
{
timers.remove(timer);
}
finally
{
_writeLock.unlock();
}
} }
} }
} }
@ -2836,25 +2826,17 @@ public class Quest extends AbstractScript implements IIdentifiable
// cancel all pending timers before reloading. // cancel all pending timers before reloading.
// if timers ought to be restarted, the quest can take care of it // if timers ought to be restarted, the quest can take care of it
// with its code (example: save global data indicating what timer must be restarted). // with its code (example: save global data indicating what timer must be restarted).
if (_questTimers != null) synchronized (_questTimers)
{ {
for (List<QuestTimer> timers : getQuestTimers().values()) for (List<QuestTimer> timers : _questTimers.values())
{ {
_readLock.lock(); for (QuestTimer timer : timers)
try
{ {
for (QuestTimer timer : timers) timer.cancel();
{
timer.cancel();
}
}
finally
{
_readLock.unlock();
} }
timers.clear(); timers.clear();
} }
getQuestTimers().clear(); _questTimers.clear();
} }
if (removeFromList) if (removeFromList)

View File

@ -23,13 +23,12 @@ import java.sql.ResultSet;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -90,10 +89,7 @@ public class Quest extends AbstractScript implements IIdentifiable
public static final Logger LOGGER = Logger.getLogger(Quest.class.getName()); public static final Logger LOGGER = Logger.getLogger(Quest.class.getName());
/** Map containing lists of timers from the name of the timer. */ /** Map containing lists of timers from the name of the timer. */
private Map<String, List<QuestTimer>> _questTimers = null; private final Map<String, List<QuestTimer>> _questTimers = new HashMap<>();
private final ReentrantReadWriteLock _rwLock = new ReentrantReadWriteLock();
private final WriteLock _writeLock = _rwLock.writeLock();
private final ReadLock _readLock = _rwLock.readLock();
/** Map containing all the start conditions. */ /** Map containing all the start conditions. */
private Set<QuestCondition> _startCondition = null; private Set<QuestCondition> _startCondition = null;
@ -255,7 +251,7 @@ public class Quest extends AbstractScript implements IIdentifiable
* Add a timer to the quest (if it doesn't exist already) and start it. * Add a timer to the quest (if it doesn't exist already) and start it.
* @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)}) * @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)})
* @param time time in ms for when to fire the timer * @param time time in ms for when to fire the timer
* @param npc the npc associated with this timer (can be null) * @param npc the NPC associated with this timer (can be null)
* @param player the player associated with this timer (can be null) * @param player the player associated with this timer (can be null)
* @see #startQuestTimer(String, long, Npc, PlayerInstance, boolean) * @see #startQuestTimer(String, long, Npc, PlayerInstance, boolean)
*/ */
@ -270,16 +266,6 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public Map<String, List<QuestTimer>> getQuestTimers() public Map<String, List<QuestTimer>> getQuestTimers()
{ {
if (_questTimers == null)
{
synchronized (this)
{
if (_questTimers == null)
{
_questTimers = new ConcurrentHashMap<>(1);
}
}
}
return _questTimers; return _questTimers;
} }
@ -287,26 +273,21 @@ public class Quest extends AbstractScript implements IIdentifiable
* Add a timer to the quest (if it doesn't exist already) and start it. * Add a timer to the quest (if it doesn't exist already) and start it.
* @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)}) * @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)})
* @param time time in ms for when to fire the timer * @param time time in ms for when to fire the timer
* @param npc the npc associated with this timer (can be null) * @param npc the NPC associated with this timer (can be null)
* @param player the player associated with this timer (can be null) * @param player the player associated with this timer (can be null)
* @param repeating indicates whether the timer is repeatable or one-time.<br> * @param repeating indicates whether the timer is repeatable or one-time.<br>
* If {@code true}, the task is repeated every {@code time} milliseconds until explicitly stopped. * If {@code true}, the task is repeated every {@code time} milliseconds until explicitly stopped.
*/ */
public void startQuestTimer(String name, long time, Npc npc, PlayerInstance player, boolean repeating) public void startQuestTimer(String name, long time, Npc npc, PlayerInstance player, boolean repeating)
{ {
final List<QuestTimer> timers = getQuestTimers().computeIfAbsent(name, k -> new ArrayList<>(1)); synchronized (_questTimers)
// If there exists a timer with this name, allow the timer only if the [npc, player] set is unique nulls act as wildcards.
if (getQuestTimer(name, npc, player) == null)
{ {
_writeLock.lock(); final List<QuestTimer> timers = _questTimers.computeIfAbsent(name, k -> new CopyOnWriteArrayList<>());
try // If there exists a timer with this name, allow the timer only if the [npc, player] set is unique nulls act as wildcards.
if (getQuestTimer(name, npc, player) == null)
{ {
timers.add(new QuestTimer(this, name, time, npc, player, repeating)); timers.add(new QuestTimer(this, name, time, npc, player, repeating));
} }
finally
{
_writeLock.unlock();
}
} }
} }
@ -319,31 +300,28 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public QuestTimer getQuestTimer(String name, Npc npc, PlayerInstance player) public QuestTimer getQuestTimer(String name, Npc npc, PlayerInstance player)
{ {
if (_questTimers == null) if (name == null)
{ {
return null; return null;
} }
final List<QuestTimer> timers = getQuestTimers().get(name); synchronized (_questTimers)
if (timers != null)
{ {
_readLock.lock(); final List<QuestTimer> timers = _questTimers.get(name);
try if ((timers == null) || timers.isEmpty())
{ {
for (QuestTimer timer : timers) return null;
}
for (QuestTimer timer : timers)
{
if ((timer != null) && timer.equals(this, name, npc, player))
{ {
if ((timer != null) && timer.equals(this, name, npc, player)) return timer;
{
return timer;
}
} }
} }
finally return null;
{
_readLock.unlock();
}
} }
return null;
} }
/** /**
@ -352,30 +330,27 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void cancelQuestTimers(String name) public void cancelQuestTimers(String name)
{ {
if (_questTimers == null) if (name == null)
{ {
return; return;
} }
final List<QuestTimer> timers = getQuestTimers().get(name); synchronized (_questTimers)
if (timers != null)
{ {
_writeLock.lock(); final List<QuestTimer> timers = _questTimers.get(name);
try if ((timers == null) || timers.isEmpty())
{ {
for (QuestTimer timer : timers) return;
}
for (QuestTimer timer : timers)
{
if (timer != null)
{ {
if (timer != null) timer.cancel();
{
timer.cancel();
}
} }
timers.clear();
}
finally
{
_writeLock.unlock();
} }
timers.clear();
} }
} }
@ -387,10 +362,28 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void cancelQuestTimer(String name, Npc npc, PlayerInstance player) public void cancelQuestTimer(String name, Npc npc, PlayerInstance player)
{ {
final QuestTimer timer = getQuestTimer(name, npc, player); if (name == null)
if (timer != null)
{ {
timer.cancel(); return;
}
synchronized (_questTimers)
{
final List<QuestTimer> timers = _questTimers.get(name);
if ((timers == null) || timers.isEmpty())
{
return;
}
for (QuestTimer timer : timers)
{
if ((timer != null) && timer.equals(this, name, npc, player))
{
timer.cancel();
timers.remove(timer);
return;
}
}
} }
} }
@ -401,20 +394,17 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void removeQuestTimer(QuestTimer timer) public void removeQuestTimer(QuestTimer timer)
{ {
if ((timer != null) && (_questTimers != null)) if (timer == null)
{ {
final List<QuestTimer> timers = getQuestTimers().get(timer.toString()); return;
}
synchronized (_questTimers)
{
final List<QuestTimer> timers = _questTimers.get(timer.toString());
if (timers != null) if (timers != null)
{ {
_writeLock.lock(); timers.remove(timer);
try
{
timers.remove(timer);
}
finally
{
_writeLock.unlock();
}
} }
} }
} }
@ -2836,25 +2826,17 @@ public class Quest extends AbstractScript implements IIdentifiable
// cancel all pending timers before reloading. // cancel all pending timers before reloading.
// if timers ought to be restarted, the quest can take care of it // if timers ought to be restarted, the quest can take care of it
// with its code (example: save global data indicating what timer must be restarted). // with its code (example: save global data indicating what timer must be restarted).
if (_questTimers != null) synchronized (_questTimers)
{ {
for (List<QuestTimer> timers : getQuestTimers().values()) for (List<QuestTimer> timers : _questTimers.values())
{ {
_readLock.lock(); for (QuestTimer timer : timers)
try
{ {
for (QuestTimer timer : timers) timer.cancel();
{
timer.cancel();
}
}
finally
{
_readLock.unlock();
} }
timers.clear(); timers.clear();
} }
getQuestTimers().clear(); _questTimers.clear();
} }
if (removeFromList) if (removeFromList)

View File

@ -23,13 +23,12 @@ import java.sql.ResultSet;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -90,10 +89,7 @@ public class Quest extends AbstractScript implements IIdentifiable
public static final Logger LOGGER = Logger.getLogger(Quest.class.getName()); public static final Logger LOGGER = Logger.getLogger(Quest.class.getName());
/** Map containing lists of timers from the name of the timer. */ /** Map containing lists of timers from the name of the timer. */
private Map<String, List<QuestTimer>> _questTimers = null; private final Map<String, List<QuestTimer>> _questTimers = new HashMap<>();
private final ReentrantReadWriteLock _rwLock = new ReentrantReadWriteLock();
private final WriteLock _writeLock = _rwLock.writeLock();
private final ReadLock _readLock = _rwLock.readLock();
/** Map containing all the start conditions. */ /** Map containing all the start conditions. */
private Set<QuestCondition> _startCondition = null; private Set<QuestCondition> _startCondition = null;
@ -255,7 +251,7 @@ public class Quest extends AbstractScript implements IIdentifiable
* Add a timer to the quest (if it doesn't exist already) and start it. * Add a timer to the quest (if it doesn't exist already) and start it.
* @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)}) * @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)})
* @param time time in ms for when to fire the timer * @param time time in ms for when to fire the timer
* @param npc the npc associated with this timer (can be null) * @param npc the NPC associated with this timer (can be null)
* @param player the player associated with this timer (can be null) * @param player the player associated with this timer (can be null)
* @see #startQuestTimer(String, long, Npc, PlayerInstance, boolean) * @see #startQuestTimer(String, long, Npc, PlayerInstance, boolean)
*/ */
@ -270,16 +266,6 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public Map<String, List<QuestTimer>> getQuestTimers() public Map<String, List<QuestTimer>> getQuestTimers()
{ {
if (_questTimers == null)
{
synchronized (this)
{
if (_questTimers == null)
{
_questTimers = new ConcurrentHashMap<>(1);
}
}
}
return _questTimers; return _questTimers;
} }
@ -287,26 +273,21 @@ public class Quest extends AbstractScript implements IIdentifiable
* Add a timer to the quest (if it doesn't exist already) and start it. * Add a timer to the quest (if it doesn't exist already) and start it.
* @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)}) * @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)})
* @param time time in ms for when to fire the timer * @param time time in ms for when to fire the timer
* @param npc the npc associated with this timer (can be null) * @param npc the NPC associated with this timer (can be null)
* @param player the player associated with this timer (can be null) * @param player the player associated with this timer (can be null)
* @param repeating indicates whether the timer is repeatable or one-time.<br> * @param repeating indicates whether the timer is repeatable or one-time.<br>
* If {@code true}, the task is repeated every {@code time} milliseconds until explicitly stopped. * If {@code true}, the task is repeated every {@code time} milliseconds until explicitly stopped.
*/ */
public void startQuestTimer(String name, long time, Npc npc, PlayerInstance player, boolean repeating) public void startQuestTimer(String name, long time, Npc npc, PlayerInstance player, boolean repeating)
{ {
final List<QuestTimer> timers = getQuestTimers().computeIfAbsent(name, k -> new ArrayList<>(1)); synchronized (_questTimers)
// If there exists a timer with this name, allow the timer only if the [npc, player] set is unique nulls act as wildcards.
if (getQuestTimer(name, npc, player) == null)
{ {
_writeLock.lock(); final List<QuestTimer> timers = _questTimers.computeIfAbsent(name, k -> new CopyOnWriteArrayList<>());
try // If there exists a timer with this name, allow the timer only if the [npc, player] set is unique nulls act as wildcards.
if (getQuestTimer(name, npc, player) == null)
{ {
timers.add(new QuestTimer(this, name, time, npc, player, repeating)); timers.add(new QuestTimer(this, name, time, npc, player, repeating));
} }
finally
{
_writeLock.unlock();
}
} }
} }
@ -319,31 +300,28 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public QuestTimer getQuestTimer(String name, Npc npc, PlayerInstance player) public QuestTimer getQuestTimer(String name, Npc npc, PlayerInstance player)
{ {
if (_questTimers == null) if (name == null)
{ {
return null; return null;
} }
final List<QuestTimer> timers = getQuestTimers().get(name); synchronized (_questTimers)
if (timers != null)
{ {
_readLock.lock(); final List<QuestTimer> timers = _questTimers.get(name);
try if ((timers == null) || timers.isEmpty())
{ {
for (QuestTimer timer : timers) return null;
}
for (QuestTimer timer : timers)
{
if ((timer != null) && timer.equals(this, name, npc, player))
{ {
if ((timer != null) && timer.equals(this, name, npc, player)) return timer;
{
return timer;
}
} }
} }
finally return null;
{
_readLock.unlock();
}
} }
return null;
} }
/** /**
@ -352,30 +330,27 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void cancelQuestTimers(String name) public void cancelQuestTimers(String name)
{ {
if (_questTimers == null) if (name == null)
{ {
return; return;
} }
final List<QuestTimer> timers = getQuestTimers().get(name); synchronized (_questTimers)
if (timers != null)
{ {
_writeLock.lock(); final List<QuestTimer> timers = _questTimers.get(name);
try if ((timers == null) || timers.isEmpty())
{ {
for (QuestTimer timer : timers) return;
}
for (QuestTimer timer : timers)
{
if (timer != null)
{ {
if (timer != null) timer.cancel();
{
timer.cancel();
}
} }
timers.clear();
}
finally
{
_writeLock.unlock();
} }
timers.clear();
} }
} }
@ -387,10 +362,28 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void cancelQuestTimer(String name, Npc npc, PlayerInstance player) public void cancelQuestTimer(String name, Npc npc, PlayerInstance player)
{ {
final QuestTimer timer = getQuestTimer(name, npc, player); if (name == null)
if (timer != null)
{ {
timer.cancel(); return;
}
synchronized (_questTimers)
{
final List<QuestTimer> timers = _questTimers.get(name);
if ((timers == null) || timers.isEmpty())
{
return;
}
for (QuestTimer timer : timers)
{
if ((timer != null) && timer.equals(this, name, npc, player))
{
timer.cancel();
timers.remove(timer);
return;
}
}
} }
} }
@ -401,20 +394,17 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void removeQuestTimer(QuestTimer timer) public void removeQuestTimer(QuestTimer timer)
{ {
if ((timer != null) && (_questTimers != null)) if (timer == null)
{ {
final List<QuestTimer> timers = getQuestTimers().get(timer.toString()); return;
}
synchronized (_questTimers)
{
final List<QuestTimer> timers = _questTimers.get(timer.toString());
if (timers != null) if (timers != null)
{ {
_writeLock.lock(); timers.remove(timer);
try
{
timers.remove(timer);
}
finally
{
_writeLock.unlock();
}
} }
} }
} }
@ -2836,25 +2826,17 @@ public class Quest extends AbstractScript implements IIdentifiable
// cancel all pending timers before reloading. // cancel all pending timers before reloading.
// if timers ought to be restarted, the quest can take care of it // if timers ought to be restarted, the quest can take care of it
// with its code (example: save global data indicating what timer must be restarted). // with its code (example: save global data indicating what timer must be restarted).
if (_questTimers != null) synchronized (_questTimers)
{ {
for (List<QuestTimer> timers : getQuestTimers().values()) for (List<QuestTimer> timers : _questTimers.values())
{ {
_readLock.lock(); for (QuestTimer timer : timers)
try
{ {
for (QuestTimer timer : timers) timer.cancel();
{
timer.cancel();
}
}
finally
{
_readLock.unlock();
} }
timers.clear(); timers.clear();
} }
getQuestTimers().clear(); _questTimers.clear();
} }
if (removeFromList) if (removeFromList)

View File

@ -23,13 +23,12 @@ import java.sql.ResultSet;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -90,10 +89,7 @@ public class Quest extends AbstractScript implements IIdentifiable
public static final Logger LOGGER = Logger.getLogger(Quest.class.getName()); public static final Logger LOGGER = Logger.getLogger(Quest.class.getName());
/** Map containing lists of timers from the name of the timer. */ /** Map containing lists of timers from the name of the timer. */
private Map<String, List<QuestTimer>> _questTimers = null; private final Map<String, List<QuestTimer>> _questTimers = new HashMap<>();
private final ReentrantReadWriteLock _rwLock = new ReentrantReadWriteLock();
private final WriteLock _writeLock = _rwLock.writeLock();
private final ReadLock _readLock = _rwLock.readLock();
/** Map containing all the start conditions. */ /** Map containing all the start conditions. */
private Set<QuestCondition> _startCondition = null; private Set<QuestCondition> _startCondition = null;
@ -265,7 +261,7 @@ public class Quest extends AbstractScript implements IIdentifiable
* Add a timer to the quest (if it doesn't exist already) and start it. * Add a timer to the quest (if it doesn't exist already) and start it.
* @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)}) * @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)})
* @param time time in ms for when to fire the timer * @param time time in ms for when to fire the timer
* @param npc the npc associated with this timer (can be null) * @param npc the NPC associated with this timer (can be null)
* @param player the player associated with this timer (can be null) * @param player the player associated with this timer (can be null)
* @see #startQuestTimer(String, long, Npc, PlayerInstance, boolean) * @see #startQuestTimer(String, long, Npc, PlayerInstance, boolean)
*/ */
@ -280,16 +276,6 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public Map<String, List<QuestTimer>> getQuestTimers() public Map<String, List<QuestTimer>> getQuestTimers()
{ {
if (_questTimers == null)
{
synchronized (this)
{
if (_questTimers == null)
{
_questTimers = new ConcurrentHashMap<>(1);
}
}
}
return _questTimers; return _questTimers;
} }
@ -297,26 +283,21 @@ public class Quest extends AbstractScript implements IIdentifiable
* Add a timer to the quest (if it doesn't exist already) and start it. * Add a timer to the quest (if it doesn't exist already) and start it.
* @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)}) * @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)})
* @param time time in ms for when to fire the timer * @param time time in ms for when to fire the timer
* @param npc the npc associated with this timer (can be null) * @param npc the NPC associated with this timer (can be null)
* @param player the player associated with this timer (can be null) * @param player the player associated with this timer (can be null)
* @param repeating indicates whether the timer is repeatable or one-time.<br> * @param repeating indicates whether the timer is repeatable or one-time.<br>
* If {@code true}, the task is repeated every {@code time} milliseconds until explicitly stopped. * If {@code true}, the task is repeated every {@code time} milliseconds until explicitly stopped.
*/ */
public void startQuestTimer(String name, long time, Npc npc, PlayerInstance player, boolean repeating) public void startQuestTimer(String name, long time, Npc npc, PlayerInstance player, boolean repeating)
{ {
final List<QuestTimer> timers = getQuestTimers().computeIfAbsent(name, k -> new ArrayList<>(1)); synchronized (_questTimers)
// If there exists a timer with this name, allow the timer only if the [npc, player] set is unique nulls act as wildcards.
if (getQuestTimer(name, npc, player) == null)
{ {
_writeLock.lock(); final List<QuestTimer> timers = _questTimers.computeIfAbsent(name, k -> new CopyOnWriteArrayList<>());
try // If there exists a timer with this name, allow the timer only if the [npc, player] set is unique nulls act as wildcards.
if (getQuestTimer(name, npc, player) == null)
{ {
timers.add(new QuestTimer(this, name, time, npc, player, repeating)); timers.add(new QuestTimer(this, name, time, npc, player, repeating));
} }
finally
{
_writeLock.unlock();
}
} }
} }
@ -329,31 +310,28 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public QuestTimer getQuestTimer(String name, Npc npc, PlayerInstance player) public QuestTimer getQuestTimer(String name, Npc npc, PlayerInstance player)
{ {
if (_questTimers == null) if (name == null)
{ {
return null; return null;
} }
final List<QuestTimer> timers = getQuestTimers().get(name); synchronized (_questTimers)
if (timers != null)
{ {
_readLock.lock(); final List<QuestTimer> timers = _questTimers.get(name);
try if ((timers == null) || timers.isEmpty())
{ {
for (QuestTimer timer : timers) return null;
}
for (QuestTimer timer : timers)
{
if ((timer != null) && timer.equals(this, name, npc, player))
{ {
if ((timer != null) && timer.equals(this, name, npc, player)) return timer;
{
return timer;
}
} }
} }
finally return null;
{
_readLock.unlock();
}
} }
return null;
} }
/** /**
@ -362,30 +340,27 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void cancelQuestTimers(String name) public void cancelQuestTimers(String name)
{ {
if (_questTimers == null) if (name == null)
{ {
return; return;
} }
final List<QuestTimer> timers = getQuestTimers().get(name); synchronized (_questTimers)
if (timers != null)
{ {
_writeLock.lock(); final List<QuestTimer> timers = _questTimers.get(name);
try if ((timers == null) || timers.isEmpty())
{ {
for (QuestTimer timer : timers) return;
}
for (QuestTimer timer : timers)
{
if (timer != null)
{ {
if (timer != null) timer.cancel();
{
timer.cancel();
}
} }
timers.clear();
}
finally
{
_writeLock.unlock();
} }
timers.clear();
} }
} }
@ -397,10 +372,28 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void cancelQuestTimer(String name, Npc npc, PlayerInstance player) public void cancelQuestTimer(String name, Npc npc, PlayerInstance player)
{ {
final QuestTimer timer = getQuestTimer(name, npc, player); if (name == null)
if (timer != null)
{ {
timer.cancel(); return;
}
synchronized (_questTimers)
{
final List<QuestTimer> timers = _questTimers.get(name);
if ((timers == null) || timers.isEmpty())
{
return;
}
for (QuestTimer timer : timers)
{
if ((timer != null) && timer.equals(this, name, npc, player))
{
timer.cancel();
timers.remove(timer);
return;
}
}
} }
} }
@ -411,20 +404,17 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void removeQuestTimer(QuestTimer timer) public void removeQuestTimer(QuestTimer timer)
{ {
if ((timer != null) && (_questTimers != null)) if (timer == null)
{ {
final List<QuestTimer> timers = getQuestTimers().get(timer.toString()); return;
}
synchronized (_questTimers)
{
final List<QuestTimer> timers = _questTimers.get(timer.toString());
if (timers != null) if (timers != null)
{ {
_writeLock.lock(); timers.remove(timer);
try
{
timers.remove(timer);
}
finally
{
_writeLock.unlock();
}
} }
} }
} }
@ -2846,25 +2836,17 @@ public class Quest extends AbstractScript implements IIdentifiable
// cancel all pending timers before reloading. // cancel all pending timers before reloading.
// if timers ought to be restarted, the quest can take care of it // if timers ought to be restarted, the quest can take care of it
// with its code (example: save global data indicating what timer must be restarted). // with its code (example: save global data indicating what timer must be restarted).
if (_questTimers != null) synchronized (_questTimers)
{ {
for (List<QuestTimer> timers : getQuestTimers().values()) for (List<QuestTimer> timers : _questTimers.values())
{ {
_readLock.lock(); for (QuestTimer timer : timers)
try
{ {
for (QuestTimer timer : timers) timer.cancel();
{
timer.cancel();
}
}
finally
{
_readLock.unlock();
} }
timers.clear(); timers.clear();
} }
getQuestTimers().clear(); _questTimers.clear();
} }
if (removeFromList) if (removeFromList)

View File

@ -27,6 +27,7 @@ import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -64,10 +65,10 @@ public class Quest extends ManagedScript
private static final String HTML_NONE_AVAILABLE = "<html><body>You are either not on a quest that involves this NPC, or you don't meet this NPC's minimum quest requirements.</body></html>"; private static final String HTML_NONE_AVAILABLE = "<html><body>You are either not on a quest that involves this NPC, or you don't meet this NPC's minimum quest requirements.</body></html>";
private static final String HTML_ALREADY_COMPLETED = "<html><body>This quest has already been completed.</body></html>"; private static final String HTML_ALREADY_COMPLETED = "<html><body>This quest has already been completed.</body></html>";
/** HashMap containing events from String value of the event */ /** Map containing events from String value of the event */
private static Map<String, Quest> _allEventsS = new HashMap<>(); private static Map<String, Quest> _allEvents = new HashMap<>();
/** HashMap containing lists of timers from the name of the timer */ /** Map containing lists of timers from the name of the timer */
private final Map<String, ArrayList<QuestTimer>> _allEventTimers = new HashMap<>(); private final Map<String, List<QuestTimer>> _questTimers = new HashMap<>();
private final int _questId; private final int _questId;
private final String _prefixPath; // used only for admin_quest_reload private final String _prefixPath; // used only for admin_quest_reload
@ -140,7 +141,7 @@ public class Quest extends ManagedScript
*/ */
public static Collection<Quest> findAllEvents() public static Collection<Quest> findAllEvents()
{ {
return _allEventsS.values(); return _allEvents.values();
} }
/** /**
@ -168,7 +169,7 @@ public class Quest extends ManagedScript
} }
else else
{ {
_allEventsS.put(getName(), this); _allEvents.put(getName(), this);
} }
initGlobalData(); initGlobalData();
@ -317,10 +318,12 @@ public class Quest extends ManagedScript
} }
/** /**
* @param name * Add a timer to the quest (if it doesn't exist already) and start it.
* @param time * @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, NpcInstance, PlayerInstance)})
* @param npc * @param time time in ms for when to fire the timer
* @param player * @param npc the NPC associated with this timer (can be null)
* @param player the player associated with this timer (can be null)
* @see #startQuestTimer(String, long, NpcInstance, PlayerInstance, boolean)
*/ */
public void startQuestTimer(String name, long time, NpcInstance npc, PlayerInstance player) public void startQuestTimer(String name, long time, NpcInstance npc, PlayerInstance player)
{ {
@ -328,53 +331,43 @@ public class Quest extends ManagedScript
} }
/** /**
* Add a timer to the quest, if it doesn't exist already. If the timer is repeatable, it will auto-fire automatically, at a fixed rate, until explicitly canceled. * Gets the quest timers.
* @param name name of the timer (also passed back as "event" in onAdvEvent) * @return the quest timers
* @param time time in ms for when to fire the timer
* @param npc npc associated with this timer (can be null)
* @param player player associated with this timer (can be null)
* @param repeating indicates if the timer is repeatable or one-time.
*/ */
public synchronized void startQuestTimer(String name, long time, NpcInstance npc, PlayerInstance player, boolean repeating) public Map<String, List<QuestTimer>> getQuestTimers()
{ {
synchronized (_allEventTimers) return _questTimers;
}
/**
* Add a timer to the quest (if it doesn't exist already) and start it.
* @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, NpcInstance, PlayerInstance)})
* @param time time in ms for when to fire the timer
* @param npc the NPC associated with this timer (can be null)
* @param player the player associated with this timer (can be null)
* @param repeating indicates whether the timer is repeatable or one-time.<br>
* If {@code true}, the task is repeated every {@code time} milliseconds until explicitly stopped.
*/
public void startQuestTimer(String name, long time, NpcInstance npc, PlayerInstance player, boolean repeating)
{
synchronized (_questTimers)
{ {
ArrayList<QuestTimer> timers = _allEventTimers.get(name); final List<QuestTimer> timers = _questTimers.computeIfAbsent(name, k -> new CopyOnWriteArrayList<>());
// If there exists a timer with this name, allow the timer only if the [npc, player] set is unique nulls act as wildcards.
// no timer exists with the same name, at all if (getQuestTimer(name, npc, player) == null)
if (timers == null)
{ {
timers = new ArrayList<>();
timers.add(new QuestTimer(this, name, time, npc, player, repeating)); timers.add(new QuestTimer(this, name, time, npc, player, repeating));
// a timer with this name exists, but may not be for the same set of npc and player
} }
else
{
QuestTimer timer = null;
for (int i = 0; i < timers.size(); i++)
{
final QuestTimer tmp = timers.get(i);
if ((tmp != null) && tmp.equals(this, name, npc, player))
{
timer = tmp;
break;
}
}
// if there exists a timer with this name, allow the timer only if the [npc, player] set is unique
// nulls act as wildcards
if (timer == null)
{
timers.add(new QuestTimer(this, name, time, npc, player, repeating));
}
}
_allEventTimers.put(name, timers);
} }
} }
/**
* Get a quest timer that matches the provided name and parameters.
* @param name the name of the quest timer to get
* @param npc the NPC associated with the quest timer to get
* @param player the player associated with the quest timer to get
* @return the quest timer that matches the specified parameters or {@code null} if nothing was found
*/
public QuestTimer getQuestTimer(String name, NpcInstance npc, PlayerInstance player) public QuestTimer getQuestTimer(String name, NpcInstance npc, PlayerInstance player)
{ {
if (name == null) if (name == null)
@ -382,19 +375,16 @@ public class Quest extends ManagedScript
return null; return null;
} }
synchronized (_allEventTimers) synchronized (_questTimers)
{ {
final ArrayList<QuestTimer> qt = _allEventTimers.get(name); final List<QuestTimer> timers = _questTimers.get(name);
if ((timers == null) || timers.isEmpty())
if ((qt == null) || qt.isEmpty())
{ {
return null; return null;
} }
for (int i = 0; i < qt.size(); i++) for (QuestTimer timer : timers)
{ {
final QuestTimer timer = qt.get(i);
if ((timer != null) && timer.equals(this, name, npc, player)) if ((timer != null) && timer.equals(this, name, npc, player))
{ {
return timer; return timer;
@ -404,34 +394,10 @@ public class Quest extends ManagedScript
} }
} }
public void cancelQuestTimer(String name, NpcInstance npc, PlayerInstance player) /**
{ * Cancel all quest timers with the specified name.
if (name == null) * @param name the name of the quest timers to cancel
{ */
return;
}
synchronized (_allEventTimers)
{
final ArrayList<QuestTimer> qt = _allEventTimers.get(name);
if ((qt == null) || qt.isEmpty())
{
return;
}
for (int i = 0; i < qt.size(); i++)
{
final QuestTimer timer = qt.get(i);
if ((timer != null) && timer.equals(this, name, npc, player))
{
timer.cancel();
qt.remove(timer);
return;
}
}
}
}
public void cancelQuestTimers(String name) public void cancelQuestTimers(String name)
{ {
if (name == null) if (name == null)
@ -439,11 +405,10 @@ public class Quest extends ManagedScript
return; return;
} }
synchronized (_allEventTimers) synchronized (_questTimers)
{ {
final ArrayList<QuestTimer> timers = _allEventTimers.get(name); final List<QuestTimer> timers = _questTimers.get(name);
if ((timers == null) || timers.isEmpty())
if (timers == null)
{ {
return; return;
} }
@ -455,11 +420,48 @@ public class Quest extends ManagedScript
timer.cancel(); timer.cancel();
} }
} }
timers.clear(); timers.clear();
} }
} }
/**
* Cancel the quest timer that matches the specified name and parameters.
* @param name the name of the quest timer to cancel
* @param npc the NPC associated with the quest timer to cancel
* @param player the player associated with the quest timer to cancel
*/
public void cancelQuestTimer(String name, NpcInstance npc, PlayerInstance player)
{
if (name == null)
{
return;
}
synchronized (_questTimers)
{
final List<QuestTimer> timers = _questTimers.get(name);
if ((timers == null) || timers.isEmpty())
{
return;
}
for (QuestTimer timer : timers)
{
if ((timer != null) && timer.equals(this, name, npc, player))
{
timer.cancel();
timers.remove(timer);
return;
}
}
}
}
/**
* Remove a quest timer from the list of all timers.<br>
* Note: does not stop the timer itself!
* @param timer the {@link QuestState} object to remove
*/
public void removeQuestTimer(QuestTimer timer) public void removeQuestTimer(QuestTimer timer)
{ {
if (timer == null) if (timer == null)
@ -467,20 +469,18 @@ public class Quest extends ManagedScript
return; return;
} }
synchronized (_allEventTimers) synchronized (_questTimers)
{ {
final ArrayList<QuestTimer> timers = _allEventTimers.get(timer.toString()); final List<QuestTimer> timers = _questTimers.get(timer.toString());
if (timers != null)
if (timers == null)
{ {
return; timers.remove(timer);
} }
timers.remove(timer);
} }
} }
// these are methods to call from java // These are methods to call within the core to call the quest events.
public boolean notifyAttack(NpcInstance npc, PlayerInstance attacker, int damage, boolean isPet) public boolean notifyAttack(NpcInstance npc, PlayerInstance attacker, int damage, boolean isPet)
{ {
String res = null; String res = null;
@ -1121,7 +1121,7 @@ public class Quest extends ManagedScript
} }
// events // events
for (String name : _allEventsS.keySet()) for (String name : _allEvents.keySet())
{ {
player.processQuestEvent(name, "enter"); player.processQuestEvent(name, "enter");
} }
@ -1797,16 +1797,17 @@ public class Quest extends ManagedScript
saveGlobalData(); saveGlobalData();
// Cancel all pending timers before reloading. // Cancel all pending timers before reloading.
// If timers ought to be restarted, the quest can take care of it with its code (example: save global data indicating what timer must be restarted). // If timers ought to be restarted, the quest can take care of it with its code (example: save global data indicating what timer must be restarted).
synchronized (_allEventTimers) synchronized (_questTimers)
{ {
for (ArrayList<QuestTimer> timers : _allEventTimers.values()) for (List<QuestTimer> timers : _questTimers.values())
{ {
for (QuestTimer timer : timers) for (QuestTimer timer : timers)
{ {
timer.cancel(); timer.cancel();
} }
timers.clear();
} }
_allEventTimers.clear(); _questTimers.clear();
} }
return QuestManager.getInstance().removeQuest(this); return QuestManager.getInstance().removeQuest(this);

View File

@ -22,14 +22,12 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.logging.Level; import java.util.logging.Level;
@ -75,10 +73,7 @@ import org.l2jmobius.gameserver.util.Util;
public class Quest extends AbstractScript implements IIdentifiable public class Quest extends AbstractScript implements IIdentifiable
{ {
/** Map containing lists of timers from the name of the timer. */ /** Map containing lists of timers from the name of the timer. */
private Map<String, List<QuestTimer>> _questTimers = null; private final Map<String, List<QuestTimer>> _questTimers = new HashMap<>();
private final ReentrantReadWriteLock _rwLock = new ReentrantReadWriteLock();
private final WriteLock _writeLock = _rwLock.writeLock();
private final ReadLock _readLock = _rwLock.readLock();
/** Map containing all the start conditions. */ /** Map containing all the start conditions. */
private Map<Predicate<PlayerInstance>, String> _startCondition = null; private Map<Predicate<PlayerInstance>, String> _startCondition = null;
@ -228,7 +223,7 @@ public class Quest extends AbstractScript implements IIdentifiable
* Add a timer to the quest (if it doesn't exist already) and start it. * Add a timer to the quest (if it doesn't exist already) and start it.
* @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)}) * @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)})
* @param time time in ms for when to fire the timer * @param time time in ms for when to fire the timer
* @param npc the npc associated with this timer (can be null) * @param npc the NPC associated with this timer (can be null)
* @param player the player associated with this timer (can be null) * @param player the player associated with this timer (can be null)
* @see #startQuestTimer(String, long, Npc, PlayerInstance, boolean) * @see #startQuestTimer(String, long, Npc, PlayerInstance, boolean)
*/ */
@ -243,16 +238,6 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public Map<String, List<QuestTimer>> getQuestTimers() public Map<String, List<QuestTimer>> getQuestTimers()
{ {
if (_questTimers == null)
{
synchronized (this)
{
if (_questTimers == null)
{
_questTimers = new ConcurrentHashMap<>(1);
}
}
}
return _questTimers; return _questTimers;
} }
@ -260,26 +245,21 @@ public class Quest extends AbstractScript implements IIdentifiable
* Add a timer to the quest (if it doesn't exist already) and start it. * Add a timer to the quest (if it doesn't exist already) and start it.
* @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)}) * @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)})
* @param time time in ms for when to fire the timer * @param time time in ms for when to fire the timer
* @param npc the npc associated with this timer (can be null) * @param npc the NPC associated with this timer (can be null)
* @param player the player associated with this timer (can be null) * @param player the player associated with this timer (can be null)
* @param repeating indicates whether the timer is repeatable or one-time.<br> * @param repeating indicates whether the timer is repeatable or one-time.<br>
* If {@code true}, the task is repeated every {@code time} milliseconds until explicitly stopped. * If {@code true}, the task is repeated every {@code time} milliseconds until explicitly stopped.
*/ */
public void startQuestTimer(String name, long time, Npc npc, PlayerInstance player, boolean repeating) public void startQuestTimer(String name, long time, Npc npc, PlayerInstance player, boolean repeating)
{ {
final List<QuestTimer> timers = getQuestTimers().computeIfAbsent(name, k -> new ArrayList<>(1)); synchronized (_questTimers)
// If there exists a timer with this name, allow the timer only if the [npc, player] set is unique nulls act as wildcards.
if (getQuestTimer(name, npc, player) == null)
{ {
_writeLock.lock(); final List<QuestTimer> timers = _questTimers.computeIfAbsent(name, k -> new CopyOnWriteArrayList<>());
try // If there exists a timer with this name, allow the timer only if the [npc, player] set is unique nulls act as wildcards.
if (getQuestTimer(name, npc, player) == null)
{ {
timers.add(new QuestTimer(this, name, time, npc, player, repeating)); timers.add(new QuestTimer(this, name, time, npc, player, repeating));
} }
finally
{
_writeLock.unlock();
}
} }
} }
@ -292,31 +272,28 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public QuestTimer getQuestTimer(String name, Npc npc, PlayerInstance player) public QuestTimer getQuestTimer(String name, Npc npc, PlayerInstance player)
{ {
if (_questTimers == null) if (name == null)
{ {
return null; return null;
} }
final List<QuestTimer> timers = getQuestTimers().get(name); synchronized (_questTimers)
if (timers != null)
{ {
_readLock.lock(); final List<QuestTimer> timers = _questTimers.get(name);
try if ((timers == null) || timers.isEmpty())
{ {
for (QuestTimer timer : timers) return null;
}
for (QuestTimer timer : timers)
{
if ((timer != null) && timer.equals(this, name, npc, player))
{ {
if ((timer != null) && timer.equals(this, name, npc, player)) return timer;
{
return timer;
}
} }
} }
finally return null;
{
_readLock.unlock();
}
} }
return null;
} }
/** /**
@ -325,30 +302,27 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void cancelQuestTimers(String name) public void cancelQuestTimers(String name)
{ {
if (_questTimers == null) if (name == null)
{ {
return; return;
} }
final List<QuestTimer> timers = getQuestTimers().get(name); synchronized (_questTimers)
if (timers != null)
{ {
_writeLock.lock(); final List<QuestTimer> timers = _questTimers.get(name);
try if ((timers == null) || timers.isEmpty())
{ {
for (QuestTimer timer : timers) return;
}
for (QuestTimer timer : timers)
{
if (timer != null)
{ {
if (timer != null) timer.cancel();
{
timer.cancel();
}
} }
timers.clear();
}
finally
{
_writeLock.unlock();
} }
timers.clear();
} }
} }
@ -360,10 +334,28 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void cancelQuestTimer(String name, Npc npc, PlayerInstance player) public void cancelQuestTimer(String name, Npc npc, PlayerInstance player)
{ {
final QuestTimer timer = getQuestTimer(name, npc, player); if (name == null)
if (timer != null)
{ {
timer.cancel(); return;
}
synchronized (_questTimers)
{
final List<QuestTimer> timers = _questTimers.get(name);
if ((timers == null) || timers.isEmpty())
{
return;
}
for (QuestTimer timer : timers)
{
if ((timer != null) && timer.equals(this, name, npc, player))
{
timer.cancel();
timers.remove(timer);
return;
}
}
} }
} }
@ -374,20 +366,17 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void removeQuestTimer(QuestTimer timer) public void removeQuestTimer(QuestTimer timer)
{ {
if ((timer != null) && (_questTimers != null)) if (timer == null)
{ {
final List<QuestTimer> timers = getQuestTimers().get(timer.toString()); return;
}
synchronized (_questTimers)
{
final List<QuestTimer> timers = _questTimers.get(timer.toString());
if (timers != null) if (timers != null)
{ {
_writeLock.lock(); timers.remove(timer);
try
{
timers.remove(timer);
}
finally
{
_writeLock.unlock();
}
} }
} }
} }
@ -2523,25 +2512,17 @@ public class Quest extends AbstractScript implements IIdentifiable
// cancel all pending timers before reloading. // cancel all pending timers before reloading.
// if timers ought to be restarted, the quest can take care of it // if timers ought to be restarted, the quest can take care of it
// with its code (example: save global data indicating what timer must be restarted). // with its code (example: save global data indicating what timer must be restarted).
if (_questTimers != null) synchronized (_questTimers)
{ {
for (List<QuestTimer> timers : getQuestTimers().values()) for (List<QuestTimer> timers : _questTimers.values())
{ {
_readLock.lock(); for (QuestTimer timer : timers)
try
{ {
for (QuestTimer timer : timers) timer.cancel();
{
timer.cancel();
}
}
finally
{
_readLock.unlock();
} }
timers.clear(); timers.clear();
} }
getQuestTimers().clear(); _questTimers.clear();
} }
if (removeFromList) if (removeFromList)

View File

@ -22,14 +22,12 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.logging.Level; import java.util.logging.Level;
@ -77,10 +75,7 @@ import org.l2jmobius.gameserver.util.Util;
public class Quest extends AbstractScript implements IIdentifiable public class Quest extends AbstractScript implements IIdentifiable
{ {
/** Map containing lists of timers from the name of the timer. */ /** Map containing lists of timers from the name of the timer. */
private Map<String, List<QuestTimer>> _questTimers = null; private final Map<String, List<QuestTimer>> _questTimers = new HashMap<>();
private final ReentrantReadWriteLock _rwLock = new ReentrantReadWriteLock();
private final WriteLock _writeLock = _rwLock.writeLock();
private final ReadLock _readLock = _rwLock.readLock();
/** Map containing all the start conditions. */ /** Map containing all the start conditions. */
private Map<Predicate<PlayerInstance>, String> _startCondition = null; private Map<Predicate<PlayerInstance>, String> _startCondition = null;
@ -230,7 +225,7 @@ public class Quest extends AbstractScript implements IIdentifiable
* Add a timer to the quest (if it doesn't exist already) and start it. * Add a timer to the quest (if it doesn't exist already) and start it.
* @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)}) * @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)})
* @param time time in ms for when to fire the timer * @param time time in ms for when to fire the timer
* @param npc the npc associated with this timer (can be null) * @param npc the NPC associated with this timer (can be null)
* @param player the player associated with this timer (can be null) * @param player the player associated with this timer (can be null)
* @see #startQuestTimer(String, long, Npc, PlayerInstance, boolean) * @see #startQuestTimer(String, long, Npc, PlayerInstance, boolean)
*/ */
@ -245,16 +240,6 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public Map<String, List<QuestTimer>> getQuestTimers() public Map<String, List<QuestTimer>> getQuestTimers()
{ {
if (_questTimers == null)
{
synchronized (this)
{
if (_questTimers == null)
{
_questTimers = new ConcurrentHashMap<>(1);
}
}
}
return _questTimers; return _questTimers;
} }
@ -262,26 +247,21 @@ public class Quest extends AbstractScript implements IIdentifiable
* Add a timer to the quest (if it doesn't exist already) and start it. * Add a timer to the quest (if it doesn't exist already) and start it.
* @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)}) * @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)})
* @param time time in ms for when to fire the timer * @param time time in ms for when to fire the timer
* @param npc the npc associated with this timer (can be null) * @param npc the NPC associated with this timer (can be null)
* @param player the player associated with this timer (can be null) * @param player the player associated with this timer (can be null)
* @param repeating indicates whether the timer is repeatable or one-time.<br> * @param repeating indicates whether the timer is repeatable or one-time.<br>
* If {@code true}, the task is repeated every {@code time} milliseconds until explicitly stopped. * If {@code true}, the task is repeated every {@code time} milliseconds until explicitly stopped.
*/ */
public void startQuestTimer(String name, long time, Npc npc, PlayerInstance player, boolean repeating) public void startQuestTimer(String name, long time, Npc npc, PlayerInstance player, boolean repeating)
{ {
final List<QuestTimer> timers = getQuestTimers().computeIfAbsent(name, k -> new ArrayList<>(1)); synchronized (_questTimers)
// If there exists a timer with this name, allow the timer only if the [npc, player] set is unique nulls act as wildcards.
if (getQuestTimer(name, npc, player) == null)
{ {
_writeLock.lock(); final List<QuestTimer> timers = _questTimers.computeIfAbsent(name, k -> new CopyOnWriteArrayList<>());
try // If there exists a timer with this name, allow the timer only if the [npc, player] set is unique nulls act as wildcards.
if (getQuestTimer(name, npc, player) == null)
{ {
timers.add(new QuestTimer(this, name, time, npc, player, repeating)); timers.add(new QuestTimer(this, name, time, npc, player, repeating));
} }
finally
{
_writeLock.unlock();
}
} }
} }
@ -294,31 +274,28 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public QuestTimer getQuestTimer(String name, Npc npc, PlayerInstance player) public QuestTimer getQuestTimer(String name, Npc npc, PlayerInstance player)
{ {
if (_questTimers == null) if (name == null)
{ {
return null; return null;
} }
final List<QuestTimer> timers = getQuestTimers().get(name); synchronized (_questTimers)
if (timers != null)
{ {
_readLock.lock(); final List<QuestTimer> timers = _questTimers.get(name);
try if ((timers == null) || timers.isEmpty())
{ {
for (QuestTimer timer : timers) return null;
}
for (QuestTimer timer : timers)
{
if ((timer != null) && timer.equals(this, name, npc, player))
{ {
if ((timer != null) && timer.equals(this, name, npc, player)) return timer;
{
return timer;
}
} }
} }
finally return null;
{
_readLock.unlock();
}
} }
return null;
} }
/** /**
@ -327,30 +304,27 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void cancelQuestTimers(String name) public void cancelQuestTimers(String name)
{ {
if (_questTimers == null) if (name == null)
{ {
return; return;
} }
final List<QuestTimer> timers = getQuestTimers().get(name); synchronized (_questTimers)
if (timers != null)
{ {
_writeLock.lock(); final List<QuestTimer> timers = _questTimers.get(name);
try if ((timers == null) || timers.isEmpty())
{ {
for (QuestTimer timer : timers) return;
}
for (QuestTimer timer : timers)
{
if (timer != null)
{ {
if (timer != null) timer.cancel();
{
timer.cancel();
}
} }
timers.clear();
}
finally
{
_writeLock.unlock();
} }
timers.clear();
} }
} }
@ -362,10 +336,28 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void cancelQuestTimer(String name, Npc npc, PlayerInstance player) public void cancelQuestTimer(String name, Npc npc, PlayerInstance player)
{ {
final QuestTimer timer = getQuestTimer(name, npc, player); if (name == null)
if (timer != null)
{ {
timer.cancel(); return;
}
synchronized (_questTimers)
{
final List<QuestTimer> timers = _questTimers.get(name);
if ((timers == null) || timers.isEmpty())
{
return;
}
for (QuestTimer timer : timers)
{
if ((timer != null) && timer.equals(this, name, npc, player))
{
timer.cancel();
timers.remove(timer);
return;
}
}
} }
} }
@ -376,20 +368,17 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void removeQuestTimer(QuestTimer timer) public void removeQuestTimer(QuestTimer timer)
{ {
if ((timer != null) && (_questTimers != null)) if (timer == null)
{ {
final List<QuestTimer> timers = getQuestTimers().get(timer.toString()); return;
}
synchronized (_questTimers)
{
final List<QuestTimer> timers = _questTimers.get(timer.toString());
if (timers != null) if (timers != null)
{ {
_writeLock.lock(); timers.remove(timer);
try
{
timers.remove(timer);
}
finally
{
_writeLock.unlock();
}
} }
} }
} }
@ -2566,25 +2555,17 @@ public class Quest extends AbstractScript implements IIdentifiable
// cancel all pending timers before reloading. // cancel all pending timers before reloading.
// if timers ought to be restarted, the quest can take care of it // if timers ought to be restarted, the quest can take care of it
// with its code (example: save global data indicating what timer must be restarted). // with its code (example: save global data indicating what timer must be restarted).
if (_questTimers != null) synchronized (_questTimers)
{ {
for (List<QuestTimer> timers : getQuestTimers().values()) for (List<QuestTimer> timers : _questTimers.values())
{ {
_readLock.lock(); for (QuestTimer timer : timers)
try
{ {
for (QuestTimer timer : timers) timer.cancel();
{
timer.cancel();
}
}
finally
{
_readLock.unlock();
} }
timers.clear(); timers.clear();
} }
getQuestTimers().clear(); _questTimers.clear();
} }
if (removeFromList) if (removeFromList)

View File

@ -23,13 +23,12 @@ import java.sql.ResultSet;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -87,10 +86,7 @@ public class Quest extends AbstractScript implements IIdentifiable
public static final Logger LOGGER = Logger.getLogger(Quest.class.getName()); public static final Logger LOGGER = Logger.getLogger(Quest.class.getName());
/** Map containing lists of timers from the name of the timer. */ /** Map containing lists of timers from the name of the timer. */
private Map<String, List<QuestTimer>> _questTimers = null; private final Map<String, List<QuestTimer>> _questTimers = new HashMap<>();
private final ReentrantReadWriteLock _rwLock = new ReentrantReadWriteLock();
private final WriteLock _writeLock = _rwLock.writeLock();
private final ReadLock _readLock = _rwLock.readLock();
/** Map containing all the start conditions. */ /** Map containing all the start conditions. */
private Set<QuestCondition> _startCondition = null; private Set<QuestCondition> _startCondition = null;
@ -253,7 +249,7 @@ public class Quest extends AbstractScript implements IIdentifiable
* Add a timer to the quest (if it doesn't exist already) and start it. * Add a timer to the quest (if it doesn't exist already) and start it.
* @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)}) * @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)})
* @param time time in ms for when to fire the timer * @param time time in ms for when to fire the timer
* @param npc the npc associated with this timer (can be null) * @param npc the NPC associated with this timer (can be null)
* @param player the player associated with this timer (can be null) * @param player the player associated with this timer (can be null)
* @see #startQuestTimer(String, long, Npc, PlayerInstance, boolean) * @see #startQuestTimer(String, long, Npc, PlayerInstance, boolean)
*/ */
@ -268,16 +264,6 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public Map<String, List<QuestTimer>> getQuestTimers() public Map<String, List<QuestTimer>> getQuestTimers()
{ {
if (_questTimers == null)
{
synchronized (this)
{
if (_questTimers == null)
{
_questTimers = new ConcurrentHashMap<>(1);
}
}
}
return _questTimers; return _questTimers;
} }
@ -285,26 +271,21 @@ public class Quest extends AbstractScript implements IIdentifiable
* Add a timer to the quest (if it doesn't exist already) and start it. * Add a timer to the quest (if it doesn't exist already) and start it.
* @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)}) * @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)})
* @param time time in ms for when to fire the timer * @param time time in ms for when to fire the timer
* @param npc the npc associated with this timer (can be null) * @param npc the NPC associated with this timer (can be null)
* @param player the player associated with this timer (can be null) * @param player the player associated with this timer (can be null)
* @param repeating indicates whether the timer is repeatable or one-time.<br> * @param repeating indicates whether the timer is repeatable or one-time.<br>
* If {@code true}, the task is repeated every {@code time} milliseconds until explicitly stopped. * If {@code true}, the task is repeated every {@code time} milliseconds until explicitly stopped.
*/ */
public void startQuestTimer(String name, long time, Npc npc, PlayerInstance player, boolean repeating) public void startQuestTimer(String name, long time, Npc npc, PlayerInstance player, boolean repeating)
{ {
final List<QuestTimer> timers = getQuestTimers().computeIfAbsent(name, k -> new ArrayList<>(1)); synchronized (_questTimers)
// If there exists a timer with this name, allow the timer only if the [npc, player] set is unique nulls act as wildcards.
if (getQuestTimer(name, npc, player) == null)
{ {
_writeLock.lock(); final List<QuestTimer> timers = _questTimers.computeIfAbsent(name, k -> new CopyOnWriteArrayList<>());
try // If there exists a timer with this name, allow the timer only if the [npc, player] set is unique nulls act as wildcards.
if (getQuestTimer(name, npc, player) == null)
{ {
timers.add(new QuestTimer(this, name, time, npc, player, repeating)); timers.add(new QuestTimer(this, name, time, npc, player, repeating));
} }
finally
{
_writeLock.unlock();
}
} }
} }
@ -317,31 +298,28 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public QuestTimer getQuestTimer(String name, Npc npc, PlayerInstance player) public QuestTimer getQuestTimer(String name, Npc npc, PlayerInstance player)
{ {
if (_questTimers == null) if (name == null)
{ {
return null; return null;
} }
final List<QuestTimer> timers = getQuestTimers().get(name); synchronized (_questTimers)
if (timers != null)
{ {
_readLock.lock(); final List<QuestTimer> timers = _questTimers.get(name);
try if ((timers == null) || timers.isEmpty())
{ {
for (QuestTimer timer : timers) return null;
}
for (QuestTimer timer : timers)
{
if ((timer != null) && timer.equals(this, name, npc, player))
{ {
if ((timer != null) && timer.equals(this, name, npc, player)) return timer;
{
return timer;
}
} }
} }
finally return null;
{
_readLock.unlock();
}
} }
return null;
} }
/** /**
@ -350,30 +328,27 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void cancelQuestTimers(String name) public void cancelQuestTimers(String name)
{ {
if (_questTimers == null) if (name == null)
{ {
return; return;
} }
final List<QuestTimer> timers = getQuestTimers().get(name); synchronized (_questTimers)
if (timers != null)
{ {
_writeLock.lock(); final List<QuestTimer> timers = _questTimers.get(name);
try if ((timers == null) || timers.isEmpty())
{ {
for (QuestTimer timer : timers) return;
}
for (QuestTimer timer : timers)
{
if (timer != null)
{ {
if (timer != null) timer.cancel();
{
timer.cancel();
}
} }
timers.clear();
}
finally
{
_writeLock.unlock();
} }
timers.clear();
} }
} }
@ -385,10 +360,28 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void cancelQuestTimer(String name, Npc npc, PlayerInstance player) public void cancelQuestTimer(String name, Npc npc, PlayerInstance player)
{ {
final QuestTimer timer = getQuestTimer(name, npc, player); if (name == null)
if (timer != null)
{ {
timer.cancel(); return;
}
synchronized (_questTimers)
{
final List<QuestTimer> timers = _questTimers.get(name);
if ((timers == null) || timers.isEmpty())
{
return;
}
for (QuestTimer timer : timers)
{
if ((timer != null) && timer.equals(this, name, npc, player))
{
timer.cancel();
timers.remove(timer);
return;
}
}
} }
} }
@ -399,20 +392,17 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void removeQuestTimer(QuestTimer timer) public void removeQuestTimer(QuestTimer timer)
{ {
if ((timer != null) && (_questTimers != null)) if (timer == null)
{ {
final List<QuestTimer> timers = getQuestTimers().get(timer.toString()); return;
}
synchronized (_questTimers)
{
final List<QuestTimer> timers = _questTimers.get(timer.toString());
if (timers != null) if (timers != null)
{ {
_writeLock.lock(); timers.remove(timer);
try
{
timers.remove(timer);
}
finally
{
_writeLock.unlock();
}
} }
} }
} }
@ -2790,25 +2780,17 @@ public class Quest extends AbstractScript implements IIdentifiable
// cancel all pending timers before reloading. // cancel all pending timers before reloading.
// if timers ought to be restarted, the quest can take care of it // if timers ought to be restarted, the quest can take care of it
// with its code (example: save global data indicating what timer must be restarted). // with its code (example: save global data indicating what timer must be restarted).
if (_questTimers != null) synchronized (_questTimers)
{ {
for (List<QuestTimer> timers : getQuestTimers().values()) for (List<QuestTimer> timers : _questTimers.values())
{ {
_readLock.lock(); for (QuestTimer timer : timers)
try
{ {
for (QuestTimer timer : timers) timer.cancel();
{
timer.cancel();
}
}
finally
{
_readLock.unlock();
} }
timers.clear(); timers.clear();
} }
getQuestTimers().clear(); _questTimers.clear();
} }
if (removeFromList) if (removeFromList)

View File

@ -23,13 +23,12 @@ import java.sql.ResultSet;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -87,10 +86,7 @@ public class Quest extends AbstractScript implements IIdentifiable
public static final Logger LOGGER = Logger.getLogger(Quest.class.getName()); public static final Logger LOGGER = Logger.getLogger(Quest.class.getName());
/** Map containing lists of timers from the name of the timer. */ /** Map containing lists of timers from the name of the timer. */
private Map<String, List<QuestTimer>> _questTimers = null; private final Map<String, List<QuestTimer>> _questTimers = new HashMap<>();
private final ReentrantReadWriteLock _rwLock = new ReentrantReadWriteLock();
private final WriteLock _writeLock = _rwLock.writeLock();
private final ReadLock _readLock = _rwLock.readLock();
/** Map containing all the start conditions. */ /** Map containing all the start conditions. */
private Set<QuestCondition> _startCondition = null; private Set<QuestCondition> _startCondition = null;
@ -253,7 +249,7 @@ public class Quest extends AbstractScript implements IIdentifiable
* Add a timer to the quest (if it doesn't exist already) and start it. * Add a timer to the quest (if it doesn't exist already) and start it.
* @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)}) * @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)})
* @param time time in ms for when to fire the timer * @param time time in ms for when to fire the timer
* @param npc the npc associated with this timer (can be null) * @param npc the NPC associated with this timer (can be null)
* @param player the player associated with this timer (can be null) * @param player the player associated with this timer (can be null)
* @see #startQuestTimer(String, long, Npc, PlayerInstance, boolean) * @see #startQuestTimer(String, long, Npc, PlayerInstance, boolean)
*/ */
@ -268,16 +264,6 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public Map<String, List<QuestTimer>> getQuestTimers() public Map<String, List<QuestTimer>> getQuestTimers()
{ {
if (_questTimers == null)
{
synchronized (this)
{
if (_questTimers == null)
{
_questTimers = new ConcurrentHashMap<>(1);
}
}
}
return _questTimers; return _questTimers;
} }
@ -285,26 +271,21 @@ public class Quest extends AbstractScript implements IIdentifiable
* Add a timer to the quest (if it doesn't exist already) and start it. * Add a timer to the quest (if it doesn't exist already) and start it.
* @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)}) * @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)})
* @param time time in ms for when to fire the timer * @param time time in ms for when to fire the timer
* @param npc the npc associated with this timer (can be null) * @param npc the NPC associated with this timer (can be null)
* @param player the player associated with this timer (can be null) * @param player the player associated with this timer (can be null)
* @param repeating indicates whether the timer is repeatable or one-time.<br> * @param repeating indicates whether the timer is repeatable or one-time.<br>
* If {@code true}, the task is repeated every {@code time} milliseconds until explicitly stopped. * If {@code true}, the task is repeated every {@code time} milliseconds until explicitly stopped.
*/ */
public void startQuestTimer(String name, long time, Npc npc, PlayerInstance player, boolean repeating) public void startQuestTimer(String name, long time, Npc npc, PlayerInstance player, boolean repeating)
{ {
final List<QuestTimer> timers = getQuestTimers().computeIfAbsent(name, k -> new ArrayList<>(1)); synchronized (_questTimers)
// If there exists a timer with this name, allow the timer only if the [npc, player] set is unique nulls act as wildcards.
if (getQuestTimer(name, npc, player) == null)
{ {
_writeLock.lock(); final List<QuestTimer> timers = _questTimers.computeIfAbsent(name, k -> new CopyOnWriteArrayList<>());
try // If there exists a timer with this name, allow the timer only if the [npc, player] set is unique nulls act as wildcards.
if (getQuestTimer(name, npc, player) == null)
{ {
timers.add(new QuestTimer(this, name, time, npc, player, repeating)); timers.add(new QuestTimer(this, name, time, npc, player, repeating));
} }
finally
{
_writeLock.unlock();
}
} }
} }
@ -317,31 +298,28 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public QuestTimer getQuestTimer(String name, Npc npc, PlayerInstance player) public QuestTimer getQuestTimer(String name, Npc npc, PlayerInstance player)
{ {
if (_questTimers == null) if (name == null)
{ {
return null; return null;
} }
final List<QuestTimer> timers = getQuestTimers().get(name); synchronized (_questTimers)
if (timers != null)
{ {
_readLock.lock(); final List<QuestTimer> timers = _questTimers.get(name);
try if ((timers == null) || timers.isEmpty())
{ {
for (QuestTimer timer : timers) return null;
}
for (QuestTimer timer : timers)
{
if ((timer != null) && timer.equals(this, name, npc, player))
{ {
if ((timer != null) && timer.equals(this, name, npc, player)) return timer;
{
return timer;
}
} }
} }
finally return null;
{
_readLock.unlock();
}
} }
return null;
} }
/** /**
@ -350,30 +328,27 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void cancelQuestTimers(String name) public void cancelQuestTimers(String name)
{ {
if (_questTimers == null) if (name == null)
{ {
return; return;
} }
final List<QuestTimer> timers = getQuestTimers().get(name); synchronized (_questTimers)
if (timers != null)
{ {
_writeLock.lock(); final List<QuestTimer> timers = _questTimers.get(name);
try if ((timers == null) || timers.isEmpty())
{ {
for (QuestTimer timer : timers) return;
}
for (QuestTimer timer : timers)
{
if (timer != null)
{ {
if (timer != null) timer.cancel();
{
timer.cancel();
}
} }
timers.clear();
}
finally
{
_writeLock.unlock();
} }
timers.clear();
} }
} }
@ -385,10 +360,28 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void cancelQuestTimer(String name, Npc npc, PlayerInstance player) public void cancelQuestTimer(String name, Npc npc, PlayerInstance player)
{ {
final QuestTimer timer = getQuestTimer(name, npc, player); if (name == null)
if (timer != null)
{ {
timer.cancel(); return;
}
synchronized (_questTimers)
{
final List<QuestTimer> timers = _questTimers.get(name);
if ((timers == null) || timers.isEmpty())
{
return;
}
for (QuestTimer timer : timers)
{
if ((timer != null) && timer.equals(this, name, npc, player))
{
timer.cancel();
timers.remove(timer);
return;
}
}
} }
} }
@ -399,20 +392,17 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void removeQuestTimer(QuestTimer timer) public void removeQuestTimer(QuestTimer timer)
{ {
if ((timer != null) && (_questTimers != null)) if (timer == null)
{ {
final List<QuestTimer> timers = getQuestTimers().get(timer.toString()); return;
}
synchronized (_questTimers)
{
final List<QuestTimer> timers = _questTimers.get(timer.toString());
if (timers != null) if (timers != null)
{ {
_writeLock.lock(); timers.remove(timer);
try
{
timers.remove(timer);
}
finally
{
_writeLock.unlock();
}
} }
} }
} }
@ -2790,25 +2780,17 @@ public class Quest extends AbstractScript implements IIdentifiable
// cancel all pending timers before reloading. // cancel all pending timers before reloading.
// if timers ought to be restarted, the quest can take care of it // if timers ought to be restarted, the quest can take care of it
// with its code (example: save global data indicating what timer must be restarted). // with its code (example: save global data indicating what timer must be restarted).
if (_questTimers != null) synchronized (_questTimers)
{ {
for (List<QuestTimer> timers : getQuestTimers().values()) for (List<QuestTimer> timers : _questTimers.values())
{ {
_readLock.lock(); for (QuestTimer timer : timers)
try
{ {
for (QuestTimer timer : timers) timer.cancel();
{
timer.cancel();
}
}
finally
{
_readLock.unlock();
} }
timers.clear(); timers.clear();
} }
getQuestTimers().clear(); _questTimers.clear();
} }
if (removeFromList) if (removeFromList)

View File

@ -23,13 +23,12 @@ import java.sql.ResultSet;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -87,10 +86,7 @@ public class Quest extends AbstractScript implements IIdentifiable
public static final Logger LOGGER = Logger.getLogger(Quest.class.getName()); public static final Logger LOGGER = Logger.getLogger(Quest.class.getName());
/** Map containing lists of timers from the name of the timer. */ /** Map containing lists of timers from the name of the timer. */
private Map<String, List<QuestTimer>> _questTimers = null; private final Map<String, List<QuestTimer>> _questTimers = new HashMap<>();
private final ReentrantReadWriteLock _rwLock = new ReentrantReadWriteLock();
private final WriteLock _writeLock = _rwLock.writeLock();
private final ReadLock _readLock = _rwLock.readLock();
/** Map containing all the start conditions. */ /** Map containing all the start conditions. */
private Set<QuestCondition> _startCondition = null; private Set<QuestCondition> _startCondition = null;
@ -253,7 +249,7 @@ public class Quest extends AbstractScript implements IIdentifiable
* Add a timer to the quest (if it doesn't exist already) and start it. * Add a timer to the quest (if it doesn't exist already) and start it.
* @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)}) * @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)})
* @param time time in ms for when to fire the timer * @param time time in ms for when to fire the timer
* @param npc the npc associated with this timer (can be null) * @param npc the NPC associated with this timer (can be null)
* @param player the player associated with this timer (can be null) * @param player the player associated with this timer (can be null)
* @see #startQuestTimer(String, long, Npc, PlayerInstance, boolean) * @see #startQuestTimer(String, long, Npc, PlayerInstance, boolean)
*/ */
@ -268,16 +264,6 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public Map<String, List<QuestTimer>> getQuestTimers() public Map<String, List<QuestTimer>> getQuestTimers()
{ {
if (_questTimers == null)
{
synchronized (this)
{
if (_questTimers == null)
{
_questTimers = new ConcurrentHashMap<>(1);
}
}
}
return _questTimers; return _questTimers;
} }
@ -285,26 +271,21 @@ public class Quest extends AbstractScript implements IIdentifiable
* Add a timer to the quest (if it doesn't exist already) and start it. * Add a timer to the quest (if it doesn't exist already) and start it.
* @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)}) * @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)})
* @param time time in ms for when to fire the timer * @param time time in ms for when to fire the timer
* @param npc the npc associated with this timer (can be null) * @param npc the NPC associated with this timer (can be null)
* @param player the player associated with this timer (can be null) * @param player the player associated with this timer (can be null)
* @param repeating indicates whether the timer is repeatable or one-time.<br> * @param repeating indicates whether the timer is repeatable or one-time.<br>
* If {@code true}, the task is repeated every {@code time} milliseconds until explicitly stopped. * If {@code true}, the task is repeated every {@code time} milliseconds until explicitly stopped.
*/ */
public void startQuestTimer(String name, long time, Npc npc, PlayerInstance player, boolean repeating) public void startQuestTimer(String name, long time, Npc npc, PlayerInstance player, boolean repeating)
{ {
final List<QuestTimer> timers = getQuestTimers().computeIfAbsent(name, k -> new ArrayList<>(1)); synchronized (_questTimers)
// If there exists a timer with this name, allow the timer only if the [npc, player] set is unique nulls act as wildcards.
if (getQuestTimer(name, npc, player) == null)
{ {
_writeLock.lock(); final List<QuestTimer> timers = _questTimers.computeIfAbsent(name, k -> new CopyOnWriteArrayList<>());
try // If there exists a timer with this name, allow the timer only if the [npc, player] set is unique nulls act as wildcards.
if (getQuestTimer(name, npc, player) == null)
{ {
timers.add(new QuestTimer(this, name, time, npc, player, repeating)); timers.add(new QuestTimer(this, name, time, npc, player, repeating));
} }
finally
{
_writeLock.unlock();
}
} }
} }
@ -317,31 +298,28 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public QuestTimer getQuestTimer(String name, Npc npc, PlayerInstance player) public QuestTimer getQuestTimer(String name, Npc npc, PlayerInstance player)
{ {
if (_questTimers == null) if (name == null)
{ {
return null; return null;
} }
final List<QuestTimer> timers = getQuestTimers().get(name); synchronized (_questTimers)
if (timers != null)
{ {
_readLock.lock(); final List<QuestTimer> timers = _questTimers.get(name);
try if ((timers == null) || timers.isEmpty())
{ {
for (QuestTimer timer : timers) return null;
}
for (QuestTimer timer : timers)
{
if ((timer != null) && timer.equals(this, name, npc, player))
{ {
if ((timer != null) && timer.equals(this, name, npc, player)) return timer;
{
return timer;
}
} }
} }
finally return null;
{
_readLock.unlock();
}
} }
return null;
} }
/** /**
@ -350,30 +328,27 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void cancelQuestTimers(String name) public void cancelQuestTimers(String name)
{ {
if (_questTimers == null) if (name == null)
{ {
return; return;
} }
final List<QuestTimer> timers = getQuestTimers().get(name); synchronized (_questTimers)
if (timers != null)
{ {
_writeLock.lock(); final List<QuestTimer> timers = _questTimers.get(name);
try if ((timers == null) || timers.isEmpty())
{ {
for (QuestTimer timer : timers) return;
}
for (QuestTimer timer : timers)
{
if (timer != null)
{ {
if (timer != null) timer.cancel();
{
timer.cancel();
}
} }
timers.clear();
}
finally
{
_writeLock.unlock();
} }
timers.clear();
} }
} }
@ -385,10 +360,28 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void cancelQuestTimer(String name, Npc npc, PlayerInstance player) public void cancelQuestTimer(String name, Npc npc, PlayerInstance player)
{ {
final QuestTimer timer = getQuestTimer(name, npc, player); if (name == null)
if (timer != null)
{ {
timer.cancel(); return;
}
synchronized (_questTimers)
{
final List<QuestTimer> timers = _questTimers.get(name);
if ((timers == null) || timers.isEmpty())
{
return;
}
for (QuestTimer timer : timers)
{
if ((timer != null) && timer.equals(this, name, npc, player))
{
timer.cancel();
timers.remove(timer);
return;
}
}
} }
} }
@ -399,20 +392,17 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void removeQuestTimer(QuestTimer timer) public void removeQuestTimer(QuestTimer timer)
{ {
if ((timer != null) && (_questTimers != null)) if (timer == null)
{ {
final List<QuestTimer> timers = getQuestTimers().get(timer.toString()); return;
}
synchronized (_questTimers)
{
final List<QuestTimer> timers = _questTimers.get(timer.toString());
if (timers != null) if (timers != null)
{ {
_writeLock.lock(); timers.remove(timer);
try
{
timers.remove(timer);
}
finally
{
_writeLock.unlock();
}
} }
} }
} }
@ -2790,25 +2780,17 @@ public class Quest extends AbstractScript implements IIdentifiable
// cancel all pending timers before reloading. // cancel all pending timers before reloading.
// if timers ought to be restarted, the quest can take care of it // if timers ought to be restarted, the quest can take care of it
// with its code (example: save global data indicating what timer must be restarted). // with its code (example: save global data indicating what timer must be restarted).
if (_questTimers != null) synchronized (_questTimers)
{ {
for (List<QuestTimer> timers : getQuestTimers().values()) for (List<QuestTimer> timers : _questTimers.values())
{ {
_readLock.lock(); for (QuestTimer timer : timers)
try
{ {
for (QuestTimer timer : timers) timer.cancel();
{
timer.cancel();
}
}
finally
{
_readLock.unlock();
} }
timers.clear(); timers.clear();
} }
getQuestTimers().clear(); _questTimers.clear();
} }
if (removeFromList) if (removeFromList)

View File

@ -23,13 +23,12 @@ import java.sql.ResultSet;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -87,10 +86,7 @@ public class Quest extends AbstractScript implements IIdentifiable
public static final Logger LOGGER = Logger.getLogger(Quest.class.getName()); public static final Logger LOGGER = Logger.getLogger(Quest.class.getName());
/** Map containing lists of timers from the name of the timer. */ /** Map containing lists of timers from the name of the timer. */
private Map<String, List<QuestTimer>> _questTimers = null; private final Map<String, List<QuestTimer>> _questTimers = new HashMap<>();
private final ReentrantReadWriteLock _rwLock = new ReentrantReadWriteLock();
private final WriteLock _writeLock = _rwLock.writeLock();
private final ReadLock _readLock = _rwLock.readLock();
/** Map containing all the start conditions. */ /** Map containing all the start conditions. */
private Set<QuestCondition> _startCondition = null; private Set<QuestCondition> _startCondition = null;
@ -253,7 +249,7 @@ public class Quest extends AbstractScript implements IIdentifiable
* Add a timer to the quest (if it doesn't exist already) and start it. * Add a timer to the quest (if it doesn't exist already) and start it.
* @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)}) * @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)})
* @param time time in ms for when to fire the timer * @param time time in ms for when to fire the timer
* @param npc the npc associated with this timer (can be null) * @param npc the NPC associated with this timer (can be null)
* @param player the player associated with this timer (can be null) * @param player the player associated with this timer (can be null)
* @see #startQuestTimer(String, long, Npc, PlayerInstance, boolean) * @see #startQuestTimer(String, long, Npc, PlayerInstance, boolean)
*/ */
@ -268,16 +264,6 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public Map<String, List<QuestTimer>> getQuestTimers() public Map<String, List<QuestTimer>> getQuestTimers()
{ {
if (_questTimers == null)
{
synchronized (this)
{
if (_questTimers == null)
{
_questTimers = new ConcurrentHashMap<>(1);
}
}
}
return _questTimers; return _questTimers;
} }
@ -285,26 +271,21 @@ public class Quest extends AbstractScript implements IIdentifiable
* Add a timer to the quest (if it doesn't exist already) and start it. * Add a timer to the quest (if it doesn't exist already) and start it.
* @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)}) * @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)})
* @param time time in ms for when to fire the timer * @param time time in ms for when to fire the timer
* @param npc the npc associated with this timer (can be null) * @param npc the NPC associated with this timer (can be null)
* @param player the player associated with this timer (can be null) * @param player the player associated with this timer (can be null)
* @param repeating indicates whether the timer is repeatable or one-time.<br> * @param repeating indicates whether the timer is repeatable or one-time.<br>
* If {@code true}, the task is repeated every {@code time} milliseconds until explicitly stopped. * If {@code true}, the task is repeated every {@code time} milliseconds until explicitly stopped.
*/ */
public void startQuestTimer(String name, long time, Npc npc, PlayerInstance player, boolean repeating) public void startQuestTimer(String name, long time, Npc npc, PlayerInstance player, boolean repeating)
{ {
final List<QuestTimer> timers = getQuestTimers().computeIfAbsent(name, k -> new ArrayList<>(1)); synchronized (_questTimers)
// If there exists a timer with this name, allow the timer only if the [npc, player] set is unique nulls act as wildcards.
if (getQuestTimer(name, npc, player) == null)
{ {
_writeLock.lock(); final List<QuestTimer> timers = _questTimers.computeIfAbsent(name, k -> new CopyOnWriteArrayList<>());
try // If there exists a timer with this name, allow the timer only if the [npc, player] set is unique nulls act as wildcards.
if (getQuestTimer(name, npc, player) == null)
{ {
timers.add(new QuestTimer(this, name, time, npc, player, repeating)); timers.add(new QuestTimer(this, name, time, npc, player, repeating));
} }
finally
{
_writeLock.unlock();
}
} }
} }
@ -317,31 +298,28 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public QuestTimer getQuestTimer(String name, Npc npc, PlayerInstance player) public QuestTimer getQuestTimer(String name, Npc npc, PlayerInstance player)
{ {
if (_questTimers == null) if (name == null)
{ {
return null; return null;
} }
final List<QuestTimer> timers = getQuestTimers().get(name); synchronized (_questTimers)
if (timers != null)
{ {
_readLock.lock(); final List<QuestTimer> timers = _questTimers.get(name);
try if ((timers == null) || timers.isEmpty())
{ {
for (QuestTimer timer : timers) return null;
}
for (QuestTimer timer : timers)
{
if ((timer != null) && timer.equals(this, name, npc, player))
{ {
if ((timer != null) && timer.equals(this, name, npc, player)) return timer;
{
return timer;
}
} }
} }
finally return null;
{
_readLock.unlock();
}
} }
return null;
} }
/** /**
@ -350,30 +328,27 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void cancelQuestTimers(String name) public void cancelQuestTimers(String name)
{ {
if (_questTimers == null) if (name == null)
{ {
return; return;
} }
final List<QuestTimer> timers = getQuestTimers().get(name); synchronized (_questTimers)
if (timers != null)
{ {
_writeLock.lock(); final List<QuestTimer> timers = _questTimers.get(name);
try if ((timers == null) || timers.isEmpty())
{ {
for (QuestTimer timer : timers) return;
}
for (QuestTimer timer : timers)
{
if (timer != null)
{ {
if (timer != null) timer.cancel();
{
timer.cancel();
}
} }
timers.clear();
}
finally
{
_writeLock.unlock();
} }
timers.clear();
} }
} }
@ -385,10 +360,28 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void cancelQuestTimer(String name, Npc npc, PlayerInstance player) public void cancelQuestTimer(String name, Npc npc, PlayerInstance player)
{ {
final QuestTimer timer = getQuestTimer(name, npc, player); if (name == null)
if (timer != null)
{ {
timer.cancel(); return;
}
synchronized (_questTimers)
{
final List<QuestTimer> timers = _questTimers.get(name);
if ((timers == null) || timers.isEmpty())
{
return;
}
for (QuestTimer timer : timers)
{
if ((timer != null) && timer.equals(this, name, npc, player))
{
timer.cancel();
timers.remove(timer);
return;
}
}
} }
} }
@ -399,20 +392,17 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void removeQuestTimer(QuestTimer timer) public void removeQuestTimer(QuestTimer timer)
{ {
if ((timer != null) && (_questTimers != null)) if (timer == null)
{ {
final List<QuestTimer> timers = getQuestTimers().get(timer.toString()); return;
}
synchronized (_questTimers)
{
final List<QuestTimer> timers = _questTimers.get(timer.toString());
if (timers != null) if (timers != null)
{ {
_writeLock.lock(); timers.remove(timer);
try
{
timers.remove(timer);
}
finally
{
_writeLock.unlock();
}
} }
} }
} }
@ -2790,25 +2780,17 @@ public class Quest extends AbstractScript implements IIdentifiable
// cancel all pending timers before reloading. // cancel all pending timers before reloading.
// if timers ought to be restarted, the quest can take care of it // if timers ought to be restarted, the quest can take care of it
// with its code (example: save global data indicating what timer must be restarted). // with its code (example: save global data indicating what timer must be restarted).
if (_questTimers != null) synchronized (_questTimers)
{ {
for (List<QuestTimer> timers : getQuestTimers().values()) for (List<QuestTimer> timers : _questTimers.values())
{ {
_readLock.lock(); for (QuestTimer timer : timers)
try
{ {
for (QuestTimer timer : timers) timer.cancel();
{
timer.cancel();
}
}
finally
{
_readLock.unlock();
} }
timers.clear(); timers.clear();
} }
getQuestTimers().clear(); _questTimers.clear();
} }
if (removeFromList) if (removeFromList)

View File

@ -23,13 +23,12 @@ import java.sql.ResultSet;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -87,10 +86,7 @@ public class Quest extends AbstractScript implements IIdentifiable
public static final Logger LOGGER = Logger.getLogger(Quest.class.getName()); public static final Logger LOGGER = Logger.getLogger(Quest.class.getName());
/** Map containing lists of timers from the name of the timer. */ /** Map containing lists of timers from the name of the timer. */
private Map<String, List<QuestTimer>> _questTimers = null; private final Map<String, List<QuestTimer>> _questTimers = new HashMap<>();
private final ReentrantReadWriteLock _rwLock = new ReentrantReadWriteLock();
private final WriteLock _writeLock = _rwLock.writeLock();
private final ReadLock _readLock = _rwLock.readLock();
/** Map containing all the start conditions. */ /** Map containing all the start conditions. */
private Set<QuestCondition> _startCondition = null; private Set<QuestCondition> _startCondition = null;
@ -253,7 +249,7 @@ public class Quest extends AbstractScript implements IIdentifiable
* Add a timer to the quest (if it doesn't exist already) and start it. * Add a timer to the quest (if it doesn't exist already) and start it.
* @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)}) * @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)})
* @param time time in ms for when to fire the timer * @param time time in ms for when to fire the timer
* @param npc the npc associated with this timer (can be null) * @param npc the NPC associated with this timer (can be null)
* @param player the player associated with this timer (can be null) * @param player the player associated with this timer (can be null)
* @see #startQuestTimer(String, long, Npc, PlayerInstance, boolean) * @see #startQuestTimer(String, long, Npc, PlayerInstance, boolean)
*/ */
@ -268,16 +264,6 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public Map<String, List<QuestTimer>> getQuestTimers() public Map<String, List<QuestTimer>> getQuestTimers()
{ {
if (_questTimers == null)
{
synchronized (this)
{
if (_questTimers == null)
{
_questTimers = new ConcurrentHashMap<>(1);
}
}
}
return _questTimers; return _questTimers;
} }
@ -285,26 +271,21 @@ public class Quest extends AbstractScript implements IIdentifiable
* Add a timer to the quest (if it doesn't exist already) and start it. * Add a timer to the quest (if it doesn't exist already) and start it.
* @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)}) * @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)})
* @param time time in ms for when to fire the timer * @param time time in ms for when to fire the timer
* @param npc the npc associated with this timer (can be null) * @param npc the NPC associated with this timer (can be null)
* @param player the player associated with this timer (can be null) * @param player the player associated with this timer (can be null)
* @param repeating indicates whether the timer is repeatable or one-time.<br> * @param repeating indicates whether the timer is repeatable or one-time.<br>
* If {@code true}, the task is repeated every {@code time} milliseconds until explicitly stopped. * If {@code true}, the task is repeated every {@code time} milliseconds until explicitly stopped.
*/ */
public void startQuestTimer(String name, long time, Npc npc, PlayerInstance player, boolean repeating) public void startQuestTimer(String name, long time, Npc npc, PlayerInstance player, boolean repeating)
{ {
final List<QuestTimer> timers = getQuestTimers().computeIfAbsent(name, k -> new ArrayList<>(1)); synchronized (_questTimers)
// If there exists a timer with this name, allow the timer only if the [npc, player] set is unique nulls act as wildcards.
if (getQuestTimer(name, npc, player) == null)
{ {
_writeLock.lock(); final List<QuestTimer> timers = _questTimers.computeIfAbsent(name, k -> new CopyOnWriteArrayList<>());
try // If there exists a timer with this name, allow the timer only if the [npc, player] set is unique nulls act as wildcards.
if (getQuestTimer(name, npc, player) == null)
{ {
timers.add(new QuestTimer(this, name, time, npc, player, repeating)); timers.add(new QuestTimer(this, name, time, npc, player, repeating));
} }
finally
{
_writeLock.unlock();
}
} }
} }
@ -317,31 +298,28 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public QuestTimer getQuestTimer(String name, Npc npc, PlayerInstance player) public QuestTimer getQuestTimer(String name, Npc npc, PlayerInstance player)
{ {
if (_questTimers == null) if (name == null)
{ {
return null; return null;
} }
final List<QuestTimer> timers = getQuestTimers().get(name); synchronized (_questTimers)
if (timers != null)
{ {
_readLock.lock(); final List<QuestTimer> timers = _questTimers.get(name);
try if ((timers == null) || timers.isEmpty())
{ {
for (QuestTimer timer : timers) return null;
}
for (QuestTimer timer : timers)
{
if ((timer != null) && timer.equals(this, name, npc, player))
{ {
if ((timer != null) && timer.equals(this, name, npc, player)) return timer;
{
return timer;
}
} }
} }
finally return null;
{
_readLock.unlock();
}
} }
return null;
} }
/** /**
@ -350,30 +328,27 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void cancelQuestTimers(String name) public void cancelQuestTimers(String name)
{ {
if (_questTimers == null) if (name == null)
{ {
return; return;
} }
final List<QuestTimer> timers = getQuestTimers().get(name); synchronized (_questTimers)
if (timers != null)
{ {
_writeLock.lock(); final List<QuestTimer> timers = _questTimers.get(name);
try if ((timers == null) || timers.isEmpty())
{ {
for (QuestTimer timer : timers) return;
}
for (QuestTimer timer : timers)
{
if (timer != null)
{ {
if (timer != null) timer.cancel();
{
timer.cancel();
}
} }
timers.clear();
}
finally
{
_writeLock.unlock();
} }
timers.clear();
} }
} }
@ -385,10 +360,28 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void cancelQuestTimer(String name, Npc npc, PlayerInstance player) public void cancelQuestTimer(String name, Npc npc, PlayerInstance player)
{ {
final QuestTimer timer = getQuestTimer(name, npc, player); if (name == null)
if (timer != null)
{ {
timer.cancel(); return;
}
synchronized (_questTimers)
{
final List<QuestTimer> timers = _questTimers.get(name);
if ((timers == null) || timers.isEmpty())
{
return;
}
for (QuestTimer timer : timers)
{
if ((timer != null) && timer.equals(this, name, npc, player))
{
timer.cancel();
timers.remove(timer);
return;
}
}
} }
} }
@ -399,20 +392,17 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void removeQuestTimer(QuestTimer timer) public void removeQuestTimer(QuestTimer timer)
{ {
if ((timer != null) && (_questTimers != null)) if (timer == null)
{ {
final List<QuestTimer> timers = getQuestTimers().get(timer.toString()); return;
}
synchronized (_questTimers)
{
final List<QuestTimer> timers = _questTimers.get(timer.toString());
if (timers != null) if (timers != null)
{ {
_writeLock.lock(); timers.remove(timer);
try
{
timers.remove(timer);
}
finally
{
_writeLock.unlock();
}
} }
} }
} }
@ -2790,25 +2780,17 @@ public class Quest extends AbstractScript implements IIdentifiable
// cancel all pending timers before reloading. // cancel all pending timers before reloading.
// if timers ought to be restarted, the quest can take care of it // if timers ought to be restarted, the quest can take care of it
// with its code (example: save global data indicating what timer must be restarted). // with its code (example: save global data indicating what timer must be restarted).
if (_questTimers != null) synchronized (_questTimers)
{ {
for (List<QuestTimer> timers : getQuestTimers().values()) for (List<QuestTimer> timers : _questTimers.values())
{ {
_readLock.lock(); for (QuestTimer timer : timers)
try
{ {
for (QuestTimer timer : timers) timer.cancel();
{
timer.cancel();
}
}
finally
{
_readLock.unlock();
} }
timers.clear(); timers.clear();
} }
getQuestTimers().clear(); _questTimers.clear();
} }
if (removeFromList) if (removeFromList)

View File

@ -23,13 +23,12 @@ import java.sql.ResultSet;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -87,10 +86,7 @@ public class Quest extends AbstractScript implements IIdentifiable
public static final Logger LOGGER = Logger.getLogger(Quest.class.getName()); public static final Logger LOGGER = Logger.getLogger(Quest.class.getName());
/** Map containing lists of timers from the name of the timer. */ /** Map containing lists of timers from the name of the timer. */
private Map<String, List<QuestTimer>> _questTimers = null; private final Map<String, List<QuestTimer>> _questTimers = new HashMap<>();
private final ReentrantReadWriteLock _rwLock = new ReentrantReadWriteLock();
private final WriteLock _writeLock = _rwLock.writeLock();
private final ReadLock _readLock = _rwLock.readLock();
/** Map containing all the start conditions. */ /** Map containing all the start conditions. */
private Set<QuestCondition> _startCondition = null; private Set<QuestCondition> _startCondition = null;
@ -253,7 +249,7 @@ public class Quest extends AbstractScript implements IIdentifiable
* Add a timer to the quest (if it doesn't exist already) and start it. * Add a timer to the quest (if it doesn't exist already) and start it.
* @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)}) * @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)})
* @param time time in ms for when to fire the timer * @param time time in ms for when to fire the timer
* @param npc the npc associated with this timer (can be null) * @param npc the NPC associated with this timer (can be null)
* @param player the player associated with this timer (can be null) * @param player the player associated with this timer (can be null)
* @see #startQuestTimer(String, long, Npc, PlayerInstance, boolean) * @see #startQuestTimer(String, long, Npc, PlayerInstance, boolean)
*/ */
@ -268,16 +264,6 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public Map<String, List<QuestTimer>> getQuestTimers() public Map<String, List<QuestTimer>> getQuestTimers()
{ {
if (_questTimers == null)
{
synchronized (this)
{
if (_questTimers == null)
{
_questTimers = new ConcurrentHashMap<>(1);
}
}
}
return _questTimers; return _questTimers;
} }
@ -285,26 +271,21 @@ public class Quest extends AbstractScript implements IIdentifiable
* Add a timer to the quest (if it doesn't exist already) and start it. * Add a timer to the quest (if it doesn't exist already) and start it.
* @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)}) * @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)})
* @param time time in ms for when to fire the timer * @param time time in ms for when to fire the timer
* @param npc the npc associated with this timer (can be null) * @param npc the NPC associated with this timer (can be null)
* @param player the player associated with this timer (can be null) * @param player the player associated with this timer (can be null)
* @param repeating indicates whether the timer is repeatable or one-time.<br> * @param repeating indicates whether the timer is repeatable or one-time.<br>
* If {@code true}, the task is repeated every {@code time} milliseconds until explicitly stopped. * If {@code true}, the task is repeated every {@code time} milliseconds until explicitly stopped.
*/ */
public void startQuestTimer(String name, long time, Npc npc, PlayerInstance player, boolean repeating) public void startQuestTimer(String name, long time, Npc npc, PlayerInstance player, boolean repeating)
{ {
final List<QuestTimer> timers = getQuestTimers().computeIfAbsent(name, k -> new ArrayList<>(1)); synchronized (_questTimers)
// If there exists a timer with this name, allow the timer only if the [npc, player] set is unique nulls act as wildcards.
if (getQuestTimer(name, npc, player) == null)
{ {
_writeLock.lock(); final List<QuestTimer> timers = _questTimers.computeIfAbsent(name, k -> new CopyOnWriteArrayList<>());
try // If there exists a timer with this name, allow the timer only if the [npc, player] set is unique nulls act as wildcards.
if (getQuestTimer(name, npc, player) == null)
{ {
timers.add(new QuestTimer(this, name, time, npc, player, repeating)); timers.add(new QuestTimer(this, name, time, npc, player, repeating));
} }
finally
{
_writeLock.unlock();
}
} }
} }
@ -317,31 +298,28 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public QuestTimer getQuestTimer(String name, Npc npc, PlayerInstance player) public QuestTimer getQuestTimer(String name, Npc npc, PlayerInstance player)
{ {
if (_questTimers == null) if (name == null)
{ {
return null; return null;
} }
final List<QuestTimer> timers = getQuestTimers().get(name); synchronized (_questTimers)
if (timers != null)
{ {
_readLock.lock(); final List<QuestTimer> timers = _questTimers.get(name);
try if ((timers == null) || timers.isEmpty())
{ {
for (QuestTimer timer : timers) return null;
}
for (QuestTimer timer : timers)
{
if ((timer != null) && timer.equals(this, name, npc, player))
{ {
if ((timer != null) && timer.equals(this, name, npc, player)) return timer;
{
return timer;
}
} }
} }
finally return null;
{
_readLock.unlock();
}
} }
return null;
} }
/** /**
@ -350,30 +328,27 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void cancelQuestTimers(String name) public void cancelQuestTimers(String name)
{ {
if (_questTimers == null) if (name == null)
{ {
return; return;
} }
final List<QuestTimer> timers = getQuestTimers().get(name); synchronized (_questTimers)
if (timers != null)
{ {
_writeLock.lock(); final List<QuestTimer> timers = _questTimers.get(name);
try if ((timers == null) || timers.isEmpty())
{ {
for (QuestTimer timer : timers) return;
}
for (QuestTimer timer : timers)
{
if (timer != null)
{ {
if (timer != null) timer.cancel();
{
timer.cancel();
}
} }
timers.clear();
}
finally
{
_writeLock.unlock();
} }
timers.clear();
} }
} }
@ -385,10 +360,28 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void cancelQuestTimer(String name, Npc npc, PlayerInstance player) public void cancelQuestTimer(String name, Npc npc, PlayerInstance player)
{ {
final QuestTimer timer = getQuestTimer(name, npc, player); if (name == null)
if (timer != null)
{ {
timer.cancel(); return;
}
synchronized (_questTimers)
{
final List<QuestTimer> timers = _questTimers.get(name);
if ((timers == null) || timers.isEmpty())
{
return;
}
for (QuestTimer timer : timers)
{
if ((timer != null) && timer.equals(this, name, npc, player))
{
timer.cancel();
timers.remove(timer);
return;
}
}
} }
} }
@ -399,20 +392,17 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void removeQuestTimer(QuestTimer timer) public void removeQuestTimer(QuestTimer timer)
{ {
if ((timer != null) && (_questTimers != null)) if (timer == null)
{ {
final List<QuestTimer> timers = getQuestTimers().get(timer.toString()); return;
}
synchronized (_questTimers)
{
final List<QuestTimer> timers = _questTimers.get(timer.toString());
if (timers != null) if (timers != null)
{ {
_writeLock.lock(); timers.remove(timer);
try
{
timers.remove(timer);
}
finally
{
_writeLock.unlock();
}
} }
} }
} }
@ -2790,25 +2780,17 @@ public class Quest extends AbstractScript implements IIdentifiable
// cancel all pending timers before reloading. // cancel all pending timers before reloading.
// if timers ought to be restarted, the quest can take care of it // if timers ought to be restarted, the quest can take care of it
// with its code (example: save global data indicating what timer must be restarted). // with its code (example: save global data indicating what timer must be restarted).
if (_questTimers != null) synchronized (_questTimers)
{ {
for (List<QuestTimer> timers : getQuestTimers().values()) for (List<QuestTimer> timers : _questTimers.values())
{ {
_readLock.lock(); for (QuestTimer timer : timers)
try
{ {
for (QuestTimer timer : timers) timer.cancel();
{
timer.cancel();
}
}
finally
{
_readLock.unlock();
} }
timers.clear(); timers.clear();
} }
getQuestTimers().clear(); _questTimers.clear();
} }
if (removeFromList) if (removeFromList)

View File

@ -23,13 +23,12 @@ import java.sql.ResultSet;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -87,10 +86,7 @@ public class Quest extends AbstractScript implements IIdentifiable
public static final Logger LOGGER = Logger.getLogger(Quest.class.getName()); public static final Logger LOGGER = Logger.getLogger(Quest.class.getName());
/** Map containing lists of timers from the name of the timer. */ /** Map containing lists of timers from the name of the timer. */
private Map<String, List<QuestTimer>> _questTimers = null; private final Map<String, List<QuestTimer>> _questTimers = new HashMap<>();
private final ReentrantReadWriteLock _rwLock = new ReentrantReadWriteLock();
private final WriteLock _writeLock = _rwLock.writeLock();
private final ReadLock _readLock = _rwLock.readLock();
/** Map containing all the start conditions. */ /** Map containing all the start conditions. */
private Set<QuestCondition> _startCondition = null; private Set<QuestCondition> _startCondition = null;
@ -253,7 +249,7 @@ public class Quest extends AbstractScript implements IIdentifiable
* Add a timer to the quest (if it doesn't exist already) and start it. * Add a timer to the quest (if it doesn't exist already) and start it.
* @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)}) * @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)})
* @param time time in ms for when to fire the timer * @param time time in ms for when to fire the timer
* @param npc the npc associated with this timer (can be null) * @param npc the NPC associated with this timer (can be null)
* @param player the player associated with this timer (can be null) * @param player the player associated with this timer (can be null)
* @see #startQuestTimer(String, long, Npc, PlayerInstance, boolean) * @see #startQuestTimer(String, long, Npc, PlayerInstance, boolean)
*/ */
@ -268,16 +264,6 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public Map<String, List<QuestTimer>> getQuestTimers() public Map<String, List<QuestTimer>> getQuestTimers()
{ {
if (_questTimers == null)
{
synchronized (this)
{
if (_questTimers == null)
{
_questTimers = new ConcurrentHashMap<>(1);
}
}
}
return _questTimers; return _questTimers;
} }
@ -285,26 +271,21 @@ public class Quest extends AbstractScript implements IIdentifiable
* Add a timer to the quest (if it doesn't exist already) and start it. * Add a timer to the quest (if it doesn't exist already) and start it.
* @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)}) * @param name the name of the timer (also passed back as "event" in {@link #onAdvEvent(String, Npc, PlayerInstance)})
* @param time time in ms for when to fire the timer * @param time time in ms for when to fire the timer
* @param npc the npc associated with this timer (can be null) * @param npc the NPC associated with this timer (can be null)
* @param player the player associated with this timer (can be null) * @param player the player associated with this timer (can be null)
* @param repeating indicates whether the timer is repeatable or one-time.<br> * @param repeating indicates whether the timer is repeatable or one-time.<br>
* If {@code true}, the task is repeated every {@code time} milliseconds until explicitly stopped. * If {@code true}, the task is repeated every {@code time} milliseconds until explicitly stopped.
*/ */
public void startQuestTimer(String name, long time, Npc npc, PlayerInstance player, boolean repeating) public void startQuestTimer(String name, long time, Npc npc, PlayerInstance player, boolean repeating)
{ {
final List<QuestTimer> timers = getQuestTimers().computeIfAbsent(name, k -> new ArrayList<>(1)); synchronized (_questTimers)
// If there exists a timer with this name, allow the timer only if the [npc, player] set is unique nulls act as wildcards.
if (getQuestTimer(name, npc, player) == null)
{ {
_writeLock.lock(); final List<QuestTimer> timers = _questTimers.computeIfAbsent(name, k -> new CopyOnWriteArrayList<>());
try // If there exists a timer with this name, allow the timer only if the [npc, player] set is unique nulls act as wildcards.
if (getQuestTimer(name, npc, player) == null)
{ {
timers.add(new QuestTimer(this, name, time, npc, player, repeating)); timers.add(new QuestTimer(this, name, time, npc, player, repeating));
} }
finally
{
_writeLock.unlock();
}
} }
} }
@ -317,31 +298,28 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public QuestTimer getQuestTimer(String name, Npc npc, PlayerInstance player) public QuestTimer getQuestTimer(String name, Npc npc, PlayerInstance player)
{ {
if (_questTimers == null) if (name == null)
{ {
return null; return null;
} }
final List<QuestTimer> timers = getQuestTimers().get(name); synchronized (_questTimers)
if (timers != null)
{ {
_readLock.lock(); final List<QuestTimer> timers = _questTimers.get(name);
try if ((timers == null) || timers.isEmpty())
{ {
for (QuestTimer timer : timers) return null;
}
for (QuestTimer timer : timers)
{
if ((timer != null) && timer.equals(this, name, npc, player))
{ {
if ((timer != null) && timer.equals(this, name, npc, player)) return timer;
{
return timer;
}
} }
} }
finally return null;
{
_readLock.unlock();
}
} }
return null;
} }
/** /**
@ -350,30 +328,27 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void cancelQuestTimers(String name) public void cancelQuestTimers(String name)
{ {
if (_questTimers == null) if (name == null)
{ {
return; return;
} }
final List<QuestTimer> timers = getQuestTimers().get(name); synchronized (_questTimers)
if (timers != null)
{ {
_writeLock.lock(); final List<QuestTimer> timers = _questTimers.get(name);
try if ((timers == null) || timers.isEmpty())
{ {
for (QuestTimer timer : timers) return;
}
for (QuestTimer timer : timers)
{
if (timer != null)
{ {
if (timer != null) timer.cancel();
{
timer.cancel();
}
} }
timers.clear();
}
finally
{
_writeLock.unlock();
} }
timers.clear();
} }
} }
@ -385,10 +360,28 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void cancelQuestTimer(String name, Npc npc, PlayerInstance player) public void cancelQuestTimer(String name, Npc npc, PlayerInstance player)
{ {
final QuestTimer timer = getQuestTimer(name, npc, player); if (name == null)
if (timer != null)
{ {
timer.cancel(); return;
}
synchronized (_questTimers)
{
final List<QuestTimer> timers = _questTimers.get(name);
if ((timers == null) || timers.isEmpty())
{
return;
}
for (QuestTimer timer : timers)
{
if ((timer != null) && timer.equals(this, name, npc, player))
{
timer.cancel();
timers.remove(timer);
return;
}
}
} }
} }
@ -399,20 +392,17 @@ public class Quest extends AbstractScript implements IIdentifiable
*/ */
public void removeQuestTimer(QuestTimer timer) public void removeQuestTimer(QuestTimer timer)
{ {
if ((timer != null) && (_questTimers != null)) if (timer == null)
{ {
final List<QuestTimer> timers = getQuestTimers().get(timer.toString()); return;
}
synchronized (_questTimers)
{
final List<QuestTimer> timers = _questTimers.get(timer.toString());
if (timers != null) if (timers != null)
{ {
_writeLock.lock(); timers.remove(timer);
try
{
timers.remove(timer);
}
finally
{
_writeLock.unlock();
}
} }
} }
} }
@ -2790,25 +2780,17 @@ public class Quest extends AbstractScript implements IIdentifiable
// cancel all pending timers before reloading. // cancel all pending timers before reloading.
// if timers ought to be restarted, the quest can take care of it // if timers ought to be restarted, the quest can take care of it
// with its code (example: save global data indicating what timer must be restarted). // with its code (example: save global data indicating what timer must be restarted).
if (_questTimers != null) synchronized (_questTimers)
{ {
for (List<QuestTimer> timers : getQuestTimers().values()) for (List<QuestTimer> timers : _questTimers.values())
{ {
_readLock.lock(); for (QuestTimer timer : timers)
try
{ {
for (QuestTimer timer : timers) timer.cancel();
{
timer.cancel();
}
}
finally
{
_readLock.unlock();
} }
timers.clear(); timers.clear();
} }
getQuestTimers().clear(); _questTimers.clear();
} }
if (removeFromList) if (removeFromList)