Add TickListener component.

TickTask allows registration and calls on each tick.
This commit is contained in:
asofold 2013-01-14 04:23:39 +01:00
parent 6d245c62a7
commit 91420f9edf
3 changed files with 84 additions and 4 deletions

View File

@ -46,6 +46,7 @@ import fr.neatmonster.nocheatplus.components.NCPListener;
import fr.neatmonster.nocheatplus.components.NameSetPermState;
import fr.neatmonster.nocheatplus.components.NoCheatPlusAPI;
import fr.neatmonster.nocheatplus.components.PermStateReceiver;
import fr.neatmonster.nocheatplus.components.TickListener;
import fr.neatmonster.nocheatplus.config.ConfPaths;
import fr.neatmonster.nocheatplus.config.ConfigFile;
import fr.neatmonster.nocheatplus.config.ConfigManager;
@ -258,10 +259,15 @@ public class NoCheatPlus extends JavaPlugin implements NoCheatPlusAPI {
((INeedConfig) obj).onReload();
}
}
if (obj instanceof TickListener){
TickTask.addTickListener((TickListener) obj);
}
if (obj instanceof PermStateReceiver){
// No immediate update done.
permStateReceivers.add((PermStateReceiver) obj);
}
// Also add to DataManager, which will pick what it needs.
// TODO: This is fishy in principle, something more concise?
dataMan.addComponent(obj);
}
@ -305,6 +311,9 @@ public class NoCheatPlus extends JavaPlugin implements NoCheatPlusAPI {
if (obj instanceof PermStateReceiver){
permStateReceivers.remove((PermStateReceiver) obj);
}
if (obj instanceof TickListener){
TickTask.removeTickListener((TickListener) obj);
}
if (obj instanceof INotifyReload) {
notifyReload.remove(obj);
}

View File

@ -0,0 +1,15 @@
package fr.neatmonster.nocheatplus.components;
/**
* Can be registered with the TickTask.
* @author mc_dev
*
*/
public interface TickListener {
/**
*
* @param tick Current tick count. This might start over at 0 if reset in onEnable.
* @param timeLast Last time after processing loop. Allows to check how long the tick already took (roughly). No "system time ran backwards" check for this value.
*/
public void onTick(int tick, long timeLast);
}

View File

@ -1,5 +1,6 @@
package fr.neatmonster.nocheatplus.utilities;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.LinkedList;
@ -13,6 +14,7 @@ import fr.neatmonster.nocheatplus.NoCheatPlus;
import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.checks.ViolationData;
import fr.neatmonster.nocheatplus.checks.access.ICheckData;
import fr.neatmonster.nocheatplus.components.TickListener;
import fr.neatmonster.nocheatplus.players.DataManager;
/**
@ -54,6 +56,9 @@ public class TickTask implements Runnable {
/** Actions to execute. */
private static final List<ViolationData> delayedActions = new LinkedList<ViolationData>();
/** Tick listeners to call every tick. */
private static final List<TickListener> tickListeners = new ArrayList<TickListener>();
/** Last n tick durations, measured from run to run.*/
private static final long[] tickDurations = new long[lagMaxTicks];
@ -93,7 +98,7 @@ public class TickTask implements Runnable {
* Force executing actions.<br>
* Note: Only call from the main thread!
*/
public void executeActions() {
public static void executeActions() {
final List<ViolationData> copyActions = new LinkedList<ViolationData>();
synchronized (delayedActions) {
if (delayedActions.isEmpty()) return;
@ -158,6 +163,28 @@ public class TickTask implements Runnable {
}
}
/**
* Add a tick listener. Should be thread safe, though... why?
* @param listener
*/
public static void addTickListener(TickListener listener){
synchronized (tickListeners) {
if (locked) return;
tickListeners.add(listener);
}
}
/**
* Remove a tick listener. Should be thread safe, though... why?
* @param listener
* @return If previously contained.
*/
public static boolean removeTickListener(TickListener listener){
synchronized (tickListeners) {
return tickListeners.remove(listener);
}
}
/**
* Get the tasks tick count. It is increased with every server tick.<br>
* NOTE: Can be called from other threads.
@ -318,7 +345,7 @@ public class TickTask implements Runnable {
}
/**
* Empty queues (call after setLocked(true)
* Empty queues (better call after setLocked(true)) and tickListeners.
*/
public static void purge(){
synchronized (permissionUpdates) {
@ -327,6 +354,9 @@ public class TickTask implements Runnable {
synchronized (delayedActions) {
delayedActions.clear();
}
synchronized (tickListeners) {
tickListeners.clear();
}
}
/**
@ -345,16 +375,42 @@ public class TickTask implements Runnable {
}
//////////////////////////
// Instance methods
// Instance methods (meant private).
//////////////////////////
/**
*
* Notify all listeners.
*
*/
private final void notifyListeners() {
final List<TickListener> copyListeners = new ArrayList<TickListener>();
synchronized (tickListeners) {
// Synchronized to allow concurrent adding (!? why ?!).
// (Ignores the locked state while still running.)
copyListeners.addAll(tickListeners);
}
for (final TickListener listener : copyListeners){
try{
listener.onTick(tick, timeLast);
}
catch(Throwable t){
LogUtil.logSevere("[NoCheatPlus] (TickTask) TickListener generated an exception:");
LogUtil.logSevere(t);
}
}
}
@Override
public void run() {
tick ++;
// Now sync is forced, for the ability to lock.
// Actions.
executeActions();
// Permissions.
updatePermissions();
// Listeners.
notifyListeners();
// Measure time after heavy stuff.
final long time = System.currentTimeMillis();