Bleeding: Add data expiration management. Also remove execution history

(remove command). Expiration duration is in minutes.
This commit is contained in:
asofold 2012-09-20 20:16:00 +02:00
parent d046d1d06e
commit 4e4556624a
9 changed files with 217 additions and 12 deletions

View File

@ -35,12 +35,14 @@ import fr.neatmonster.nocheatplus.command.INotifyReload;
import fr.neatmonster.nocheatplus.config.ConfPaths;
import fr.neatmonster.nocheatplus.config.ConfigFile;
import fr.neatmonster.nocheatplus.config.ConfigManager;
import fr.neatmonster.nocheatplus.config.INeedConfig;
import fr.neatmonster.nocheatplus.hooks.NCPExemptionManager;
import fr.neatmonster.nocheatplus.metrics.Metrics;
import fr.neatmonster.nocheatplus.metrics.Metrics.Graph;
import fr.neatmonster.nocheatplus.metrics.Metrics.Plotter;
import fr.neatmonster.nocheatplus.metrics.MetricsData;
import fr.neatmonster.nocheatplus.net.NCPNetServerHandler;
import fr.neatmonster.nocheatplus.players.DataManager;
import fr.neatmonster.nocheatplus.players.Permissions;
import fr.neatmonster.nocheatplus.utilities.BlockProperties;
import fr.neatmonster.nocheatplus.utilities.LagMeasureTask;
@ -150,15 +152,26 @@ public class NoCheatPlus extends JavaPlugin implements Listener {
/** Is a new update available? */
private boolean updateAvailable = false;
/** Player data future stuff. */
protected final DataManager dataMan = new DataManager();
/**
* Convenience method to add to listeners and notifyReload lists.
* @param listener
* Convenience method to add components according to implemented interfaces,
* like Listener, INotifyReload, INeedConfig.<br>
* This must be done after the configuration has been initialized.
* @param obj
*/
private void addListener(final Listener listener){
Bukkit.getPluginManager().registerEvents(listener, this);
listeners.add(listener);
if (listener instanceof INotifyReload){
notifyReload.add((INotifyReload) listener);
private void addComponent(final Object obj){
if (obj instanceof Listener){
final Listener listener = (Listener) obj;
Bukkit.getPluginManager().registerEvents(listener, this);
listeners.add(listener);
}
if (obj instanceof INotifyReload){
notifyReload.add((INotifyReload) obj);
if (obj instanceof INeedConfig){
((INeedConfig) obj).onReload();
}
}
}
@ -216,7 +229,8 @@ public class NoCheatPlus extends JavaPlugin implements Listener {
// List the events listeners and register.
Bukkit.getPluginManager().registerEvents(this, this);
for (final Listener listener : new Listener[]{
for (final Object obj : new Object[]{
dataMan,
new BlockBreakListener(),
new BlockInteractListener(),
new BlockPlaceListener(),
@ -228,7 +242,7 @@ public class NoCheatPlus extends JavaPlugin implements Listener {
new Workarounds(),
NCPExemptionManager.getListener(),
}){
addListener(listener);
addComponent(obj);
}
// Register the commands handler.
@ -239,6 +253,13 @@ public class NoCheatPlus extends JavaPlugin implements Listener {
// Set up the tick task.
TickTask.start(this);
Bukkit.getScheduler().scheduleSyncRepeatingTask(this, new Runnable() {
@Override
public void run() {
dataMan.checkExpiration();
}
}, 1207, 1207);
ConfigFile config = ConfigManager.getConfigFile();

View File

@ -10,6 +10,7 @@ import fr.neatmonster.nocheatplus.actions.ParameterName;
import fr.neatmonster.nocheatplus.hooks.NCPExemptionManager;
import fr.neatmonster.nocheatplus.hooks.NCPHookManager;
import fr.neatmonster.nocheatplus.metrics.MetricsData;
import fr.neatmonster.nocheatplus.players.DataManager;
import fr.neatmonster.nocheatplus.players.ExecutionHistory;
import fr.neatmonster.nocheatplus.utilities.TickTask;
@ -55,6 +56,7 @@ public abstract class Check {
public Check(final CheckType type) {
this.type = type;
ViolationHistory.checkTypeMap.put(getClass().getName(), type);
DataManager.registerExecutionHistory(type, histories);
}
/**

View File

@ -161,6 +161,10 @@ public class ViolationHistory {
else
return null;
}
public static ViolationHistory removeHistory(final String playerName){
return violationHistories.remove(playerName);
}
/** The violation levels for every check. */
private final List<ViolationLevel> violationLevels = new ArrayList<ViolationLevel>();

View File

@ -11,6 +11,7 @@ import fr.neatmonster.nocheatplus.NoCheatPlus;
import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.checks.ViolationHistory;
import fr.neatmonster.nocheatplus.command.NCPCommand;
import fr.neatmonster.nocheatplus.players.DataManager;
import fr.neatmonster.nocheatplus.players.Permissions;
import fr.neatmonster.nocheatplus.utilities.CheckUtils;
@ -44,7 +45,15 @@ public class RemovePlayerCommand extends NCPCommand {
ViolationHistory hist = ViolationHistory.getHistory(playerName, false);
boolean histRemoved = false;
if (hist != null) histRemoved = hist.remove(checkType);
if (hist != null){
histRemoved = hist.remove(checkType);
if (checkType == CheckType.ALL){
histRemoved = true;
ViolationHistory.removeHistory(playerName);
}
}
if (DataManager.removeExecutionHistory(checkType, playerName)) histRemoved = true;
final boolean dataRemoved = CheckType.removeData(playerName, checkType);

View File

@ -51,6 +51,13 @@ public abstract class ConfPaths {
public static final String MISCELLANEOUS_NOMOVEDTOOQUICKLY_ENABLED = MISCELLANEOUS_NOMOVEDTOOQUICKLY + "enabled";
public static final String MISCELLANEOUS_NOMOVEDTOOQUICKLY_USEPROXY = MISCELLANEOUS_NOMOVEDTOOQUICKLY + "useproxy";
@GlobalConfig
private static final String DATA = "data.";
private static final String DATA_EXPIRATION = DATA + "expiration.";
public static final String DATA_EXPIRATION_DURATION = DATA_EXPIRATION + "duration";
public static final String DATA_EXPIRATION_DATA = DATA_EXPIRATION + "data";
public static final String DATA_EXPIRATION_HISTORY = DATA_EXPIRATION + "history";
private static final String CHECKS = "checks.";
/*

View File

@ -57,6 +57,10 @@ public class DefaultConfig extends ConfigFile {
// set(ConfPaths.MISCELLANEOUS_NOMOVEDTOOQUICKLY_ENABLED, false);
// set(ConfPaths.MISCELLANEOUS_NOMOVEDTOOQUICKLY_USEPROXY, false);
set(ConfPaths.DATA_EXPIRATION_DURATION, 0);
set(ConfPaths.DATA_EXPIRATION_HISTORY, false);
/*
* 888 88b, 888 888 888 88b, 888

View File

@ -0,0 +1,11 @@
package fr.neatmonster.nocheatplus.config;
import fr.neatmonster.nocheatplus.command.INotifyReload;
/**
* Indicate that an instance needs config after time of creation but in onEnable.
* @author mc_dev
*
*/
public interface INeedConfig extends INotifyReload{
}

View File

@ -27,6 +27,9 @@ public class APIUtils {
/** Only the children. */
private static final Map<CheckType, CheckType[]> childrenMap = new HashMap<CheckType, CheckType[]>();
/** Check including children, for convenient iteration. */
private static final Map<CheckType, CheckType[]> withChildrenMap = new HashMap<CheckType, CheckType[]>();
static {
final Map<CheckType, Set<CheckType>> map = new HashMap<CheckType, Set<CheckType>>();
@ -38,8 +41,15 @@ public class APIUtils {
if (isParent(other, type)) map.get(other).add(type);
}
}
for (final CheckType parent : map.keySet())
childrenMap.put(parent, map.get(parent).toArray(new CheckType[] {}));
for (final CheckType parent : map.keySet()){
final Set<CheckType> set = map.get(parent);
final CheckType[] a = new CheckType[set.size()];
childrenMap.put(parent, set.toArray(a));
final CheckType[] aw = new CheckType[set.size() + 1];
set.toArray(aw);
aw[set.size()] = parent;
withChildrenMap.put(parent, aw);
}
}
/**
@ -53,6 +63,18 @@ public class APIUtils {
public static final Collection<CheckType> getChildren(final CheckType type) {
return Arrays.asList(childrenMap.get(type));
}
/**
* Return an unmodifiable collection of the given check type with children. Always returns a collection, does
* contain the check type itself.
*
* @param type
* the check type
* @return the children
*/
public static final Collection<CheckType> getWithChildren(final CheckType type) {
return Arrays.asList(withChildrenMap.get(type));
}
/**
* Check if the supposed parent is ancestor of the supposed child. Does not check versus the supposed child

View File

@ -0,0 +1,125 @@
package fr.neatmonster.nocheatplus.players;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerKickEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.checks.ViolationHistory;
import fr.neatmonster.nocheatplus.checks.access.CheckDataFactory;
import fr.neatmonster.nocheatplus.command.INotifyReload;
import fr.neatmonster.nocheatplus.config.ConfPaths;
import fr.neatmonster.nocheatplus.config.ConfigFile;
import fr.neatmonster.nocheatplus.config.ConfigManager;
import fr.neatmonster.nocheatplus.config.INeedConfig;
import fr.neatmonster.nocheatplus.hooks.APIUtils;
public class DataManager implements Listener, INotifyReload, INeedConfig{
protected static DataManager instance = null;
/*
*
*/
/**
* Access order for playerName (exact) -> ms time of logout.
* <hr>
* Later this might hold central player data objects instead of the long only.
*/
private final Map<String, Long> lastLogout = new LinkedHashMap<String, Long>(50, 0.75f, true);
/**
* Execution histories of the checks.
*/
protected final Map<CheckType, Map<String, ExecutionHistory>> executionHistories = new HashMap<CheckType, Map<String,ExecutionHistory>>();
protected long durExpireData = 0;
protected boolean deleteData = true;
protected boolean deleteHistory = false;
public DataManager(){
instance = this;
}
public void checkExpiration(){
if (durExpireData <= 0) return;
final long now = System.currentTimeMillis();
final Set<CheckDataFactory> factories = new LinkedHashSet<CheckDataFactory>();
final Set<Entry<String, Long>> entries = lastLogout.entrySet();
final Iterator<Entry<String, Long>> iterator = entries.iterator();
while (iterator.hasNext()){
final Entry<String, Long> entry = iterator.next();
final long ts = entry.getValue();
if (now - ts <= durExpireData) break;
final String playerName = entry.getKey();
if (deleteData){
factories.clear();
for (final CheckType type : CheckType.values()){
final CheckDataFactory factory = type.getDataFactory();
if (factory != null) factories.add(factory);
}
for (final CheckDataFactory factory : factories){
factory.removeData(playerName);
}
}
if (deleteData || deleteHistory) removeExecutionHistory(CheckType.ALL, playerName);
if (deleteHistory) ViolationHistory.removeHistory(playerName);
iterator.remove();
}
}
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerJoin(final PlayerJoinEvent event){
lastLogout.remove(event.getPlayer().getName());
}
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerQuit(final PlayerQuitEvent event){
lastLogout.put(event.getPlayer().getName(), System.currentTimeMillis());
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPlayerKick(final PlayerKickEvent event){
lastLogout.put(event.getPlayer().getName(), System.currentTimeMillis());
}
@Override
public void onReload() {
// future
adjustSettings();
}
private void adjustSettings() {
final ConfigFile config = ConfigManager.getConfigFile();
durExpireData = config.getLong(ConfPaths.DATA_EXPIRATION_DURATION) * 60000L; // in minutes
deleteData = config.getBoolean(ConfPaths.DATA_EXPIRATION_DATA, true); // hidden.
deleteHistory = config.getBoolean(ConfPaths.DATA_EXPIRATION_HISTORY);
}
public static void registerExecutionHistory(CheckType type, Map<String, ExecutionHistory> histories) {
instance.executionHistories.put(type, histories);
}
public static boolean removeExecutionHistory(final CheckType type, final String playerName){
boolean removed = false;
// TODO: design ...
for (final CheckType refType : APIUtils.getWithChildren(type)){
final Map<String, ExecutionHistory> map = instance.executionHistories.get(refType);
if (map != null && map.remove(playerName) != null) removed = true;
}
return removed;
}
}