Add "globalchat" check skeleton - currently only monitors 30 seconds

chat frequency per player, delegate to captcha or actions, configurable
paramters.
This commit is contained in:
asofold 2012-09-01 12:22:38 +02:00
parent 8c7ade7034
commit 4508bc30e0
9 changed files with 195 additions and 2 deletions

View File

@ -54,6 +54,7 @@ public enum CheckType {
CHAT(ChatConfig.factory, ChatData.factory),
CHAT_COLOR(CHAT, Permissions.CHAT_COLOR),
CHAT_NOPWNAGE(CHAT, Permissions.CHAT_NOPWNAGE),
CHAT_GLOBALCHAT(CHAT, Permissions.CHAT_GLOBALCHAT),
FIGHT(FightConfig.factory, FightData.factory),
FIGHT_ANGLE(FIGHT, Permissions.FIGHT_ANGLE),

View File

@ -69,6 +69,12 @@ public class ChatConfig implements CheckConfig {
public final boolean colorCheck;
public final ActionList colorActions;
public final boolean globalChatCheck;
public double globalChatFrequencyFactor;
public final double globalChatFrequencyWeight;
public final double globalChatLevel;
public final ActionList globalChatActions;
public final boolean noPwnageCheck;
public final List<String> noPwnageExclusions;
@ -135,6 +141,12 @@ public class ChatConfig implements CheckConfig {
public ChatConfig(final ConfigFile data) {
colorCheck = data.getBoolean(ConfPaths.CHAT_COLOR_CHECK);
colorActions = data.getActionList(ConfPaths.CHAT_COLOR_ACTIONS, Permissions.CHAT_COLOR);
globalChatCheck = data.getBoolean(ConfPaths.CHAT_GLOBALCHAT_CHECK);
globalChatFrequencyFactor = data.getDouble(ConfPaths.CHAT_GLOBALCHAT_FREQUENCY_FACTOR);
globalChatFrequencyWeight = data.getDouble(ConfPaths.CHAT_GLOBALCHAT_FREQUENCY_WEIGHT);
globalChatLevel = data.getDouble(ConfPaths.CHAT_GLOBALCHAT_LEVEL);
globalChatActions = data.getActionList(ConfPaths.CHAT_GLOBALCHAT_ACTIONS, Permissions.CHAT_GLOBALCHAT);
noPwnageCheck = data.getBoolean(ConfPaths.CHAT_NOPWNAGE_CHECK);
noPwnageExclusions = data.getStringList(ConfPaths.CHAT_NOPWNAGE_EXCLUSIONS);
@ -201,6 +213,8 @@ public class ChatConfig implements CheckConfig {
switch (checkType) {
case CHAT_COLOR:
return colorCheck;
case CHAT_GLOBALCHAT:
return globalChatCheck;
case CHAT_NOPWNAGE:
return noPwnageCheck;
default:

View File

@ -7,6 +7,7 @@ import org.bukkit.entity.Player;
import fr.neatmonster.nocheatplus.checks.CheckData;
import fr.neatmonster.nocheatplus.checks.CheckDataFactory;
import fr.neatmonster.nocheatplus.utilities.ActionFrequency;
/*
* MM'""""'YMM dP dP M""""""'YMM dP
@ -49,7 +50,11 @@ public class ChatData implements CheckData {
// Violation levels.
public double captchaVL;
public double colorVL;
public double globalChatVL;
public double noPwnageVL;
// Data of the globalchat check.
public final ActionFrequency globalChatFrequency = new ActionFrequency(10, 3000);
// Data of the no pwnage check.
public int noPwnageCaptchTries;

View File

@ -34,6 +34,9 @@ public class ChatListener implements Listener {
/** The no pwnage check. */
private final NoPwnage noPwnage = new NoPwnage();
/** Global chat check (experiment: alternative / supplement). */
private final GlobalChat globalChat = new GlobalChat();
/**
* We listen to PlayerChat events for obvious reasons.
@ -60,7 +63,10 @@ public class ChatListener implements Listener {
// Then the no pwnage check.
if (noPwnage.check(player, event.getMessage(), false))
event.setCancelled(true);
// player.kickPlayer(CheckUtils.removeColors(ChatConfig.getConfig(player).noPwnageKickMessage));
else if (globalChat.check(player, event.getMessage(), (ICaptcha) noPwnage))
// Only check those that got through.
// (ICaptcha to start captcha if desired.)
event.setCancelled(true);
}
/**
@ -115,7 +121,6 @@ public class ChatListener implements Listener {
// Then the no pwnage check.
if (noPwnage.check(player, event.getMessage(), true))
event.setCancelled(true);
// player.kickPlayer(CheckUtils.removeColors(ChatConfig.getConfig(player).noPwnageKickMessage));
}
/**

View File

@ -0,0 +1,67 @@
package fr.neatmonster.nocheatplus.checks.chat;
import org.bukkit.entity.Player;
import fr.neatmonster.nocheatplus.checks.Check;
import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.hooks.NCPExemptionManager;
import fr.neatmonster.nocheatplus.players.Permissions;
/**
* Some alternative more or less advanced analysis methods.
* @author mc_dev
*
*/
public class GlobalChat extends Check{
public GlobalChat() {
super(CheckType.CHAT_GLOBALCHAT);
}
/**
*
* @param player
* @param message
* @param captcha Used for starting captcha on failure.
* @return
*/
public boolean check(final Player player, final String message, final ICaptcha captcha) {
// Take time once:
final long time = System.currentTimeMillis();
final ChatConfig cc = ChatConfig.getConfig(player);
// Checking the player, actually.
if (!cc.isEnabled(type) || NCPExemptionManager.isExempted(player, type))
return false;
final ChatData data = ChatData.getData(player);
boolean cancel = false;
data.globalChatFrequency.add(time);
double score = cc.globalChatFrequencyWeight * data.globalChatFrequency.getScore(cc.globalChatFrequencyFactor);
if (score < 2.0 * cc.globalChatFrequencyWeight)
// Reset the VL.
data.globalChatVL = 0.0;
// TODO Core checks....
if (score > cc.globalChatLevel){
if (captcha.shouldStartCaptcha(cc, data)){
captcha.sendNewCaptcha(player, cc, data);
cancel = true;
}
else{
data.globalChatVL += score / 10.0;
if (executeActionsThreadSafe(player, data.globalChatVL, score, cc.globalChatActions, Permissions.CHAT_GLOBALCHAT))
cancel = true;
}
}
else
data.globalChatVL *= 0.95;
return cancel;
}
}

View File

@ -138,6 +138,14 @@ public abstract class ConfPaths {
private static final String CHAT_COLOR = CHAT + "color.";
public static final String CHAT_COLOR_CHECK = CHAT_COLOR + "active";
public static final String CHAT_COLOR_ACTIONS = CHAT_COLOR + "actions";
public static final String CHAT_GLOBALCHAT = CHAT + "globalchat.";
public static final String CHAT_GLOBALCHAT_CHECK = CHAT_GLOBALCHAT + "active";
public static final String CHAT_GLOBALCHAT_FREQUENCY = CHAT_GLOBALCHAT + "frequency.";
public static final String CHAT_GLOBALCHAT_FREQUENCY_WEIGHT = CHAT_GLOBALCHAT_FREQUENCY + "weight";
public static final String CHAT_GLOBALCHAT_FREQUENCY_FACTOR = CHAT_GLOBALCHAT_FREQUENCY + "factor";
public static final String CHAT_GLOBALCHAT_LEVEL = CHAT_GLOBALCHAT + "level";
public static final String CHAT_GLOBALCHAT_ACTIONS = CHAT_GLOBALCHAT + "actions";
private static final String CHAT_NOPWNAGE = CHAT + "nopwnage.";
public static final String CHAT_NOPWNAGE_CHECK = CHAT_NOPWNAGE + "active";

View File

@ -124,6 +124,12 @@ public class DefaultConfig extends ConfigFile {
*/
set(ConfPaths.CHAT_COLOR_CHECK, true);
set(ConfPaths.CHAT_COLOR_ACTIONS, "log:color:0:1:if cancel");
set(ConfPaths.CHAT_GLOBALCHAT_CHECK, true);
set(ConfPaths.CHAT_GLOBALCHAT_FREQUENCY_FACTOR, 0.8D);
set(ConfPaths.CHAT_GLOBALCHAT_FREQUENCY_WEIGHT, 6.0D);
set(ConfPaths.CHAT_GLOBALCHAT_LEVEL, 30D);
set(ConfPaths.CHAT_GLOBALCHAT_ACTIONS, "log:globalchat:0:5:if cancel");
set(ConfPaths.CHAT_NOPWNAGE_CHECK, true);
set(ConfPaths.CHAT_NOPWNAGE_EXCLUSIONS, new ArrayList<String>());
@ -318,6 +324,7 @@ public class DefaultConfig extends ConfigFile {
+ "tried to move from [locationfrom] to [locationto] over a distance of [distance] block(s)" + end);
set(ConfPaths.STRINGS + ".freach", start + "tried to attack entity out of reach" + end);
set(ConfPaths.STRINGS + ".fspeed", start + "tried to attack more than [limit] times per second" + end);
set(ConfPaths.STRINGS + ".globalchat", start + "potentially annoying chat" + end);
set(ConfPaths.STRINGS + ".godmode", start + "avoided taking damage or lagging" + end);
set(ConfPaths.STRINGS + ".instantbow", start + "fires bow to fast" + end);
set(ConfPaths.STRINGS + ".instanteat", start + "eats food [food] too fast" + end);

View File

@ -81,6 +81,7 @@ public class Permissions {
*/
private static final String CHAT = CHECKS + ".chat";
public static final String CHAT_COLOR = CHAT + ".color";
public static final String CHAT_GLOBALCHAT = CHAT + ".globalchat";
public static final String CHAT_NOPWNAGE = CHAT + ".nopwnage";
public static final String CHAT_NOPWNAGE_CAPTCHA = CHAT_NOPWNAGE + ".captcha";

View File

@ -0,0 +1,85 @@
package fr.neatmonster.nocheatplus.utilities;
/**
* Keep track of frequency of some action,
* sort into buckets, representing time intervals,
* TODO: find a better name.
* @author mc_dev
*
*/
public class ActionFrequency {
/** Reference time for filling in. */
long time = 0;
final int[] buckets;
final long durBucket;
public ActionFrequency(final int nBuckets, final long durBucket){
this.buckets = new int[nBuckets];
this.durBucket = durBucket;
}
/**
* Update and add.
* @param ts
*/
public void add(final long now){
update(now);
}
/**
* Update without adding, also updates time.
* @param now
*/
public void update(final long now) {
final long diff = now - time;
final int shift = (int) ((float) diff / (float) durBucket);
if (shift == 0){
// No update, just fill in.
buckets[0] ++;
return;
}
else if (shift >= buckets.length){
// Clear and fill in (beyond range).
clear(now);
buckets[0] ++;
return;
}
// Update buckets.
for (int i = 0; i < buckets.length - shift; i++){
buckets[buckets.length - (i + 1)] = buckets[buckets.length - (i + 1 + shift)];
}
for (int i = 0; i < shift; i++){
buckets[i] = 0;
}
buckets[0] ++;
// Set time according to bucket duration (!).
time += durBucket * shift;
}
public void clear(final long now) {
for (int i = 0; i < buckets.length; i++){
buckets[i] = 0;
}
time = now;
}
/**
* Get a weighted sum score, weight for bucket i: w(i) = factor^i.
* @param factor
* @return
*/
public double getScore(final double factor){
double res = buckets[0];
double cf = factor;
for (int i = 1; i < buckets.length; i++){
res += cf * buckets[i];
cf *= factor;
}
return res;
}
}