diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/Check.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/Check.java
index 06dbe581..20bc7b06 100644
--- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/Check.java
+++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/Check.java
@@ -13,7 +13,6 @@ import fr.neatmonster.nocheatplus.compat.MCAccess;
import fr.neatmonster.nocheatplus.components.MCAccessHolder;
import fr.neatmonster.nocheatplus.hooks.NCPExemptionManager;
import fr.neatmonster.nocheatplus.hooks.NCPHookManager;
-import fr.neatmonster.nocheatplus.logging.LogUtil;
import fr.neatmonster.nocheatplus.players.DataManager;
import fr.neatmonster.nocheatplus.players.ExecutionHistory;
import fr.neatmonster.nocheatplus.utilities.TickTask;
@@ -41,7 +40,7 @@ public abstract class Check implements MCAccessHolder{
/** The type. */
protected final CheckType type;
-
+
protected MCAccess mcAccess;
/**
@@ -56,7 +55,7 @@ public abstract class Check implements MCAccessHolder{
ViolationHistory.checkTypeMap.put(getClass().getName(), type);
DataManager.registerExecutionHistory(type, histories);
}
-
+
/**
* Execute actions, possibly thread safe according to the isMainThread flag.
* This does not use extra synchronization.
@@ -94,7 +93,7 @@ public abstract class Check implements MCAccessHolder{
final ActionList actions) {
return executeActions(new ViolationData(this, player, vL, addedVL, actions), true);
}
-
+
/**
* Execute some actions for the specified player, only for the main thread.
*
@@ -103,7 +102,7 @@ public abstract class Check implements MCAccessHolder{
* @return true, if the event should be cancelled
*/
protected boolean executeActions(final ViolationData violationData){
- return executeActions(violationData, true);
+ return executeActions(violationData, true);
}
/**
@@ -115,24 +114,24 @@ public abstract class Check implements MCAccessHolder{
* @return true, if the event should be cancelled
*/
protected boolean executeActions(final ViolationData violationData, final boolean isMainThread) {
-
- // Dispatch the VL processing to the hook manager (now thread safe).
+
+ // Dispatch the VL processing to the hook manager (now thread safe).
if (NCPHookManager.shouldCancelVLProcessing(violationData))
// One of the hooks has decided to cancel the VL processing, return false.
return false;
-
+
final boolean hasCancel = violationData.hasCancel();
-
+
if (isMainThread)
- return violationData.executeActions();
+ return violationData.executeActions();
else
- // Always schedule to add to ViolationHistory.
- TickTask.requestActionsExecution(violationData);
-
- // (Design change: Permission checks are moved to cached permissions, lazily updated.)
- return hasCancel;
+ // Always schedule to add to ViolationHistory.
+ TickTask.requestActionsExecution(violationData);
+
+ // (Design change: Permission checks are moved to cached permissions, lazily updated.)
+ return hasCancel;
}
-
+
/**
* Fill in parameters for creating violation data.
* Individual checks should override this to fill in check specific parameters,
@@ -141,9 +140,9 @@ public abstract class Check implements MCAccessHolder{
* @return
*/
protected Map getParameterMap(final ViolationData violationData){
- final Map params = new HashMap();
- // (Standard parameters like player, vl, check name are filled in in ViolationData.getParameter!)
- return params;
+ final Map params = new HashMap();
+ // (Standard parameters like player, vl, check name are filled in in ViolationData.getParameter!)
+ return params;
}
/**
@@ -180,14 +179,14 @@ public abstract class Check implements MCAccessHolder{
return !NCPExemptionManager.isExempted(player, type);
}
- @Override
- public void setMCAccess(MCAccess mcAccess) {
- this.mcAccess = mcAccess;
- }
+ @Override
+ public void setMCAccess(MCAccess mcAccess) {
+ this.mcAccess = mcAccess;
+ }
+
+ @Override
+ public MCAccess getMCAccess() {
+ return mcAccess;
+ }
- @Override
- public MCAccess getMCAccess() {
- return mcAccess;
- }
-
}
diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/access/ACheckConfig.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/access/ACheckConfig.java
index ed5bb9d3..0e77c9bd 100644
--- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/access/ACheckConfig.java
+++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/access/ACheckConfig.java
@@ -12,13 +12,13 @@ public abstract class ACheckConfig implements ICheckConfig {
/** For on the fly debug setting. */
public boolean debug = false;
-
+
/** If to adapt to server side lag. */
public final boolean lag;
-
+
/** Permissions to hold in player data cache, not final for flexibility. */
protected String[] cachePermissions;
-
+
/**
*
* @param config
@@ -26,8 +26,8 @@ public abstract class ACheckConfig implements ICheckConfig {
*/
public ACheckConfig(final ConfigFile config, final String pathPrefix){
this(config, pathPrefix, null);
- }
-
+ }
+
/**
*
* @param config
@@ -41,11 +41,11 @@ public abstract class ACheckConfig implements ICheckConfig {
lag = config.getBoolean(pathPrefix + ConfPaths.SUB_LAG, true) && config.getBoolean(ConfPaths.MISCELLANEOUS_LAG, true);
this.cachePermissions = cachePermissions;
}
-
- @Override
- public String[] getCachePermissions() {
- return cachePermissions;
- }
+
+ @Override
+ public String[] getCachePermissions() {
+ return cachePermissions;
+ }
@Override
public boolean getDebug() {
@@ -56,5 +56,5 @@ public abstract class ACheckConfig implements ICheckConfig {
public void setDebug(final boolean debug) {
this.debug = debug;
}
-
+
}
diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/chat/Captcha.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/chat/Captcha.java
index 4d80732e..bd669f8e 100644
--- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/chat/Captcha.java
+++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/chat/Captcha.java
@@ -15,25 +15,25 @@ import fr.neatmonster.nocheatplus.utilities.ColorUtil;
*
*/
public class Captcha extends Check implements ICaptcha{
-
- public Captcha() {
- super(CheckType.CHAT_CAPTCHA);
- }
- /** The random number generator. */
+ public Captcha() {
+ super(CheckType.CHAT_CAPTCHA);
+ }
+
+ /** The random number generator. */
private final Random random = new Random();
-
+
@Override
public void checkCaptcha(Player player, String message, ChatConfig cc, ChatData data, boolean isMainThread) {
- // Correct answer to the captcha?
+ // Correct answer to the captcha?
if (message.equals(data.captchaGenerated)) {
// Yes, clear their data and do not worry anymore about them.
data.reset();
data.captchaStarted = false;
player.sendMessage(ColorUtil.replaceColors(cc.captchaSuccess));
} else {
- // Increment their tries number counter.
+ // Increment their tries number counter.
data.captchTries++;
data.captchaVL ++;
// Have they failed too man times?
@@ -45,58 +45,58 @@ public class Captcha extends Check implements ICaptcha{
// Display the question again (if not kicked).
if (player.isOnline()) {
- sendCaptcha(player, cc, data);
+ sendCaptcha(player, cc, data);
}
}
- }
+ }
@Override
- public void sendNewCaptcha(Player player, ChatConfig cc, ChatData data) {
- // Display a captcha to the player.
+ public void sendNewCaptcha(Player player, ChatConfig cc, ChatData data) {
+ // Display a captcha to the player.
generateCaptcha(cc, data, true);
sendCaptcha(player, cc, data);
data.captchaStarted = true;
- }
+ }
@Override
public void generateCaptcha(ChatConfig cc, ChatData data, boolean reset) {
- if (reset) data.captchTries = 0;
- final char[] chars = new char[cc.captchaLength];
+ if (reset) data.captchTries = 0;
+ final char[] chars = new char[cc.captchaLength];
for (int i = 0; i < cc.captchaLength; i++)
chars[i] = cc.captchaCharacters.charAt(random
.nextInt(cc.captchaCharacters.length()));
data.captchaGenerated = new String(chars);
- }
-
+ }
+
@Override
public void resetCaptcha(Player player){
- ChatData data = ChatData.getData(player);
- synchronized (data) {
- resetCaptcha(ChatConfig.getConfig(player), data);
- }
+ ChatData data = ChatData.getData(player);
+ synchronized (data) {
+ resetCaptcha(ChatConfig.getConfig(player), data);
+ }
}
-
+
@Override
public void resetCaptcha(ChatConfig cc, ChatData data){
- data.captchTries = 0;
- if (shouldCheckCaptcha(cc, data) || shouldStartCaptcha(cc, data)){
- generateCaptcha(cc, data, true);
- }
+ data.captchTries = 0;
+ if (shouldCheckCaptcha(cc, data) || shouldStartCaptcha(cc, data)){
+ generateCaptcha(cc, data, true);
+ }
}
- @Override
- public void sendCaptcha(Player player, ChatConfig cc, ChatData data) {
- player.sendMessage(ColorUtil.replaceColors(cc.captchaQuestion.replace("[captcha]",
+ @Override
+ public void sendCaptcha(Player player, ChatConfig cc, ChatData data) {
+ player.sendMessage(ColorUtil.replaceColors(cc.captchaQuestion.replace("[captcha]",
data.captchaGenerated)));
- }
+ }
@Override
- public boolean shouldStartCaptcha(ChatConfig cc, ChatData data) {
- return cc.captchaCheck && !data.captchaStarted && !data.hasCachedPermission(Permissions.CHAT_CAPTCHA);
- }
+ public boolean shouldStartCaptcha(ChatConfig cc, ChatData data) {
+ return cc.captchaCheck && !data.captchaStarted && !data.hasCachedPermission(Permissions.CHAT_CAPTCHA);
+ }
@Override
- public boolean shouldCheckCaptcha(ChatConfig cc, ChatData data) {
- return cc.captchaCheck && data.captchaStarted && !data.hasCachedPermission(Permissions.CHAT_CAPTCHA);
- }
+ public boolean shouldCheckCaptcha(ChatConfig cc, ChatData data) {
+ return cc.captchaCheck && data.captchaStarted && !data.hasCachedPermission(Permissions.CHAT_CAPTCHA);
+ }
}
diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/chat/Text.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/chat/Text.java
index 4ab1e7ee..3c36dcbd 100644
--- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/chat/Text.java
+++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/chat/Text.java
@@ -31,140 +31,140 @@ import fr.neatmonster.nocheatplus.utilities.StringUtil;
*/
public class Text extends Check implements INotifyReload {
- private LetterEngine engine = null;
-
- /** Not really cancelled but above threshold for actions. */
- private String lastCancelledMessage = "";
- private long lastCancelledTime = 0;
-
- private String lastGlobalMessage = "";
- private long lastGlobalTime = 0;
-
- public Text() {
- super(CheckType.CHAT_TEXT);
- init();
- }
+ private LetterEngine engine = null;
- /**
- * Start analysis.
- * @param player
- * The player who issued the message.
- * @param message
- * The message to check.
- * @param captcha
- * Used for starting captcha on failure, if configured so.
- * @param alreadyCancelled
- * @return
- */
- public boolean check(final Player player, final String message, final ICaptcha captcha, boolean isMainThread, final boolean alreadyCancelled) {
-
- final ChatConfig cc = ChatConfig.getConfig(player);
- final ChatData data = ChatData.getData(player);
-
- synchronized (data) {
- return unsafeCheck(player, message, captcha, cc, data, isMainThread, alreadyCancelled);
- }
- }
-
- private void init() {
- // Set some things from the global config.
- final ConfigFile config = ConfigManager.getConfigFile();
- final NoCheatPlusAPI api = NCPAPIProvider.getNoCheatPlusAPI();
- if (engine != null) {
- engine.clear();
- api.removeComponent(engine);
- }
- engine = new LetterEngine(config);
- api.addComponent(engine);
- }
+ /** Not really cancelled but above threshold for actions. */
+ private String lastCancelledMessage = "";
+ private long lastCancelledTime = 0;
- @Override
- public void onReload() {
- synchronized(engine) {
- engine.clear();
- }
- init();
- }
+ private String lastGlobalMessage = "";
+ private long lastGlobalTime = 0;
- /**
- * Check without further synchronization.
- * @param player
- * @param message
- * @param captcha
- * @param cc
- * @param data
- * @param isMainThread
- * @param alreadyCancelled
- * @return
- */
- private boolean unsafeCheck(final Player player, final String message, final ICaptcha captcha,
- final ChatConfig cc, final ChatData data, boolean isMainThread, final boolean alreadyCancelled) {
-
- // Test captcha.
- // TODO: Skip captcha for "handleaschat" commands? [controversial potential]
- if (captcha.shouldCheckCaptcha(cc, data)) {
- captcha.checkCaptcha(player, message, cc, data, isMainThread);
- return true;
- } else if (alreadyCancelled) {
- // Skip checking.
- return true;
- }
-
- // Take time once:
- final long time = System.currentTimeMillis();
-
- final String lcMessage = message.trim().toLowerCase();
-
- boolean cancel = false;
-
- boolean debug = cc.textDebug || cc.debug;
-
- final List debugParts;
- if (debug) {
- debugParts = new LinkedList();
- debugParts.add("[NoCheatPlus][chat.text] Message ("+player.getName()+"/"+message.length()+"): ");
- }
- else debugParts = null;
-
- // Update the frequency interval weights.
- data.chatFrequency.update(time);
-
- // Score for this message (violation score).
- float score = 0;
-
- final MessageLetterCount letterCounts = new MessageLetterCount(message);
-
- final int msgLen = message.length();
-
- // (Following: random/made up criteria.)
-
- // TODO: Create tests for all methods with wordlists, fake chat (refactor for that).
-
- // Full message processing. ------------
-
- // Upper case.
- if (letterCounts.fullCount.upperCase > msgLen / 3) {
- final float wUpperCase = 0.6f * letterCounts.fullCount.getUpperCaseRatio();
- score += wUpperCase * cc.textMessageUpperCase;
- }
-
- // Letters vs. word length.
- if (msgLen > 4) {
- final float fullRep = letterCounts.fullCount.getLetterCountRatio();
- // Long messages: very small and very big are bad !
- final float wRepetition = (float) msgLen / 15.0f * Math.abs(0.5f - fullRep);
- score += wRepetition * cc.textMessageLetterCount;
-
- // Number of words vs. length of message
- final float fnWords = (float) letterCounts.words.length / (float) msgLen;
- if (fnWords > 0.75f) { // TODO: balance or configure or remove ?
- score += fnWords * cc.textMessagePartition;
- }
- }
-
- final CombinedData cData = CombinedData.getData(player);
- final long timeout = 8000; // TODO: maybe set dynamically in data.
- // Repetition of last message.
+ public Text() {
+ super(CheckType.CHAT_TEXT);
+ init();
+ }
+
+ /**
+ * Start analysis.
+ * @param player
+ * The player who issued the message.
+ * @param message
+ * The message to check.
+ * @param captcha
+ * Used for starting captcha on failure, if configured so.
+ * @param alreadyCancelled
+ * @return
+ */
+ public boolean check(final Player player, final String message, final ICaptcha captcha, boolean isMainThread, final boolean alreadyCancelled) {
+
+ final ChatConfig cc = ChatConfig.getConfig(player);
+ final ChatData data = ChatData.getData(player);
+
+ synchronized (data) {
+ return unsafeCheck(player, message, captcha, cc, data, isMainThread, alreadyCancelled);
+ }
+ }
+
+ private void init() {
+ // Set some things from the global config.
+ final ConfigFile config = ConfigManager.getConfigFile();
+ final NoCheatPlusAPI api = NCPAPIProvider.getNoCheatPlusAPI();
+ if (engine != null) {
+ engine.clear();
+ api.removeComponent(engine);
+ }
+ engine = new LetterEngine(config);
+ api.addComponent(engine);
+ }
+
+ @Override
+ public void onReload() {
+ synchronized(engine) {
+ engine.clear();
+ }
+ init();
+ }
+
+ /**
+ * Check without further synchronization.
+ * @param player
+ * @param message
+ * @param captcha
+ * @param cc
+ * @param data
+ * @param isMainThread
+ * @param alreadyCancelled
+ * @return
+ */
+ private boolean unsafeCheck(final Player player, final String message, final ICaptcha captcha,
+ final ChatConfig cc, final ChatData data, boolean isMainThread, final boolean alreadyCancelled) {
+
+ // Test captcha.
+ // TODO: Skip captcha for "handleaschat" commands? [controversial potential]
+ if (captcha.shouldCheckCaptcha(cc, data)) {
+ captcha.checkCaptcha(player, message, cc, data, isMainThread);
+ return true;
+ } else if (alreadyCancelled) {
+ // Skip checking.
+ return true;
+ }
+
+ // Take time once:
+ final long time = System.currentTimeMillis();
+
+ final String lcMessage = message.trim().toLowerCase();
+
+ boolean cancel = false;
+
+ boolean debug = cc.textDebug || cc.debug;
+
+ final List debugParts;
+ if (debug) {
+ debugParts = new LinkedList();
+ debugParts.add("[NoCheatPlus][chat.text] Message ("+player.getName()+"/"+message.length()+"): ");
+ }
+ else debugParts = null;
+
+ // Update the frequency interval weights.
+ data.chatFrequency.update(time);
+
+ // Score for this message (violation score).
+ float score = 0;
+
+ final MessageLetterCount letterCounts = new MessageLetterCount(message);
+
+ final int msgLen = message.length();
+
+ // (Following: random/made up criteria.)
+
+ // TODO: Create tests for all methods with wordlists, fake chat (refactor for that).
+
+ // Full message processing. ------------
+
+ // Upper case.
+ if (letterCounts.fullCount.upperCase > msgLen / 3) {
+ final float wUpperCase = 0.6f * letterCounts.fullCount.getUpperCaseRatio();
+ score += wUpperCase * cc.textMessageUpperCase;
+ }
+
+ // Letters vs. word length.
+ if (msgLen > 4) {
+ final float fullRep = letterCounts.fullCount.getLetterCountRatio();
+ // Long messages: very small and very big are bad !
+ final float wRepetition = (float) msgLen / 15.0f * Math.abs(0.5f - fullRep);
+ score += wRepetition * cc.textMessageLetterCount;
+
+ // Number of words vs. length of message
+ final float fnWords = (float) letterCounts.words.length / (float) msgLen;
+ if (fnWords > 0.75f) { // TODO: balance or configure or remove ?
+ score += fnWords * cc.textMessagePartition;
+ }
+ }
+
+ final CombinedData cData = CombinedData.getData(player);
+ final long timeout = 8000; // TODO: maybe set dynamically in data.
+ // Repetition of last message.
if (cc.textMsgRepeatSelf != 0f && time - data.chatLastTime < timeout) {
if (StringUtil.isSimilar(lcMessage, data.chatLastMessage, 0.8f)) {
final float timeWeight = (float) (timeout - (time - data.chatLastTime)) / (float) timeout;
@@ -179,147 +179,147 @@ public class Text extends Check implements INotifyReload {
}
}
// Repetition of last cancelled message.
- if (cc.textMsgRepeatCancel != 0f && time - lastCancelledTime < timeout) {
- if (StringUtil.isSimilar(lcMessage, lastCancelledMessage, 0.8f)) {
- final float timeWeight = (float) (timeout - (time - lastCancelledTime)) / (float) timeout;
- score += cc.textMsgRepeatCancel * timeWeight;
- }
- }
- // Chat quickly after join.
- if (cc.textMsgAfterJoin != 0f && time - cData.lastJoinTime < timeout) {
- final float timeWeight = (float) (timeout - (time - cData.lastJoinTime)) / (float) timeout;
- score += cc.textMsgAfterJoin * timeWeight;
- }
- // Chat without moving.
+ if (cc.textMsgRepeatCancel != 0f && time - lastCancelledTime < timeout) {
+ if (StringUtil.isSimilar(lcMessage, lastCancelledMessage, 0.8f)) {
+ final float timeWeight = (float) (timeout - (time - lastCancelledTime)) / (float) timeout;
+ score += cc.textMsgRepeatCancel * timeWeight;
+ }
+ }
+ // Chat quickly after join.
+ if (cc.textMsgAfterJoin != 0f && time - cData.lastJoinTime < timeout) {
+ final float timeWeight = (float) (timeout - (time - cData.lastJoinTime)) / (float) timeout;
+ score += cc.textMsgAfterJoin * timeWeight;
+ }
+ // Chat without moving.
if (cc.textMsgNoMoving != 0f && time - cData.lastMoveTime > timeout) {
score += cc.textMsgNoMoving;
}
-
- // Per word checks. -------------------
- float wWords = 0.0f;
- final float avwLen = (float) msgLen / (float) letterCounts.words.length;
- for (final WordLetterCount word: letterCounts.words) {
- float wWord = 0.0f;
- final int wLen = word.word.length();
- // TODO: ? used letters vs. word length.
-
- // Length of word vs. av. word length.
- final float fLenAv = Math.abs(avwLen - (float) wLen) / avwLen;
- wWord += fLenAv * cc.textMessageLengthAv;
-
- // Length of word vs. message length;
- final float fLenMsg = (float) wLen / (float) msgLen;
- wWord += fLenMsg * cc.textMessageLengthMsg;
-
- // Not letter:
- float notLetter = word.getNotLetterRatio();
- notLetter *= notLetter;
- wWord += notLetter * cc.textMessageNoLetter;
-
- wWord *= wWord; // TODO: quadratic ? (configurable)
- wWords += wWord;
- }
- wWords /= (float) letterCounts.words.length;
- score += wWords;
-
- if (debug && score > 0f) debugParts.add("Simple score: " + StringUtil.fdec3.format(score));
-
- // Engine:
- // TODO: more fine grained sync !
- float wEngine = 0f;
- final Map engMap;
- synchronized (engine) {
- engMap = engine.process(letterCounts, player.getName(), cc, data);
- // TODO: more fine grained sync !s
- // TODO: different methods (add or max or add+max or something else).
- for (final Float res : engMap.values()) {
- if (cc.textEngineMaximum) wEngine = Math.max(wEngine, res.floatValue());
- else wEngine += res.floatValue();
- }
- }
- score += wEngine;
-
- // Wrapping it up. --------------------
- // Add weight to frequency counts.
- final float normalScore = Math.max(cc.textFreqNormMin, score);
- data.chatFrequency.add(time, normalScore);
- final float accumulated = cc.textFreqNormWeight * data.chatFrequency.score(cc.textFreqNormFactor);
- final boolean normalViolation = accumulated > cc.textFreqNormLevel;
- final float shortTermScore = Math.max(cc.textFreqShortTermMin, score);
- data.chatShortTermFrequency.add(time, shortTermScore);
- // TODO: very short term (1st bucket) or do it indirectly.
- final float shortTermAccumulated = cc.textFreqShortTermWeight * data.chatShortTermFrequency.score(cc.textFreqShortTermFactor);
- final boolean shortTermViolation = shortTermAccumulated > cc.textFreqShortTermLevel;
-
- if (normalViolation || shortTermViolation) {
- lastCancelledMessage = lcMessage;
- lastCancelledTime = time;
-
- final double added;
- if (shortTermViolation) {
- added = (shortTermAccumulated - cc.textFreqShortTermLevel)/ 3.0;
- } else {
- added = (accumulated - cc.textFreqNormLevel) / 10.0;
- }
- data.textVL += added;
-
- if (captcha.shouldStartCaptcha(cc, data)) {
- captcha.sendNewCaptcha(player, cc, data);
- cancel = true;
- }
- else{
- if (shortTermViolation) {
+ // Per word checks. -------------------
+ float wWords = 0.0f;
+ final float avwLen = (float) msgLen / (float) letterCounts.words.length;
+ for (final WordLetterCount word: letterCounts.words) {
+ float wWord = 0.0f;
+ final int wLen = word.word.length();
+ // TODO: ? used letters vs. word length.
+
+ // Length of word vs. av. word length.
+ final float fLenAv = Math.abs(avwLen - (float) wLen) / avwLen;
+ wWord += fLenAv * cc.textMessageLengthAv;
+
+ // Length of word vs. message length;
+ final float fLenMsg = (float) wLen / (float) msgLen;
+ wWord += fLenMsg * cc.textMessageLengthMsg;
+
+ // Not letter:
+ float notLetter = word.getNotLetterRatio();
+ notLetter *= notLetter;
+ wWord += notLetter * cc.textMessageNoLetter;
+
+ wWord *= wWord; // TODO: quadratic ? (configurable)
+ wWords += wWord;
+ }
+ wWords /= (float) letterCounts.words.length;
+ score += wWords;
+
+ if (debug && score > 0f) debugParts.add("Simple score: " + StringUtil.fdec3.format(score));
+
+ // Engine:
+ // TODO: more fine grained sync !
+ float wEngine = 0f;
+ final Map engMap;
+ synchronized (engine) {
+ engMap = engine.process(letterCounts, player.getName(), cc, data);
+ // TODO: more fine grained sync !s
+ // TODO: different methods (add or max or add+max or something else).
+ for (final Float res : engMap.values()) {
+ if (cc.textEngineMaximum) wEngine = Math.max(wEngine, res.floatValue());
+ else wEngine += res.floatValue();
+ }
+ }
+ score += wEngine;
+
+ // Wrapping it up. --------------------
+ // Add weight to frequency counts.
+ final float normalScore = Math.max(cc.textFreqNormMin, score);
+ data.chatFrequency.add(time, normalScore);
+ final float accumulated = cc.textFreqNormWeight * data.chatFrequency.score(cc.textFreqNormFactor);
+ final boolean normalViolation = accumulated > cc.textFreqNormLevel;
+
+ final float shortTermScore = Math.max(cc.textFreqShortTermMin, score);
+ data.chatShortTermFrequency.add(time, shortTermScore);
+ // TODO: very short term (1st bucket) or do it indirectly.
+ final float shortTermAccumulated = cc.textFreqShortTermWeight * data.chatShortTermFrequency.score(cc.textFreqShortTermFactor);
+ final boolean shortTermViolation = shortTermAccumulated > cc.textFreqShortTermLevel;
+
+ if (normalViolation || shortTermViolation) {
+ lastCancelledMessage = lcMessage;
+ lastCancelledTime = time;
+
+ final double added;
+ if (shortTermViolation) {
+ added = (shortTermAccumulated - cc.textFreqShortTermLevel)/ 3.0;
+ } else {
+ added = (accumulated - cc.textFreqNormLevel) / 10.0;
+ }
+ data.textVL += added;
+
+ if (captcha.shouldStartCaptcha(cc, data)) {
+ captcha.sendNewCaptcha(player, cc, data);
+ cancel = true;
+ }
+ else{
+ if (shortTermViolation) {
if (executeActions(player, data.textVL, added, cc.textFreqShortTermActions, isMainThread)) {
- cancel = true;
+ cancel = true;
}
}
- else if (normalViolation) {
- if (executeActions(player, data.textVL, added, cc.textFreqNormActions, isMainThread)) {
- cancel = true;
- }
- }
- }
- }
- else if (cc.chatWarningCheck && time - data.chatWarningTime > cc.chatWarningTimeout && (100f * accumulated / cc.textFreqNormLevel > cc.chatWarningLevel || 100f * shortTermAccumulated / cc.textFreqShortTermLevel > cc.chatWarningLevel)) {
- NCPAPIProvider.getNoCheatPlusAPI().sendMessageOnTick(player.getName(), ColorUtil.replaceColors(cc.chatWarningMessage));
+ else if (normalViolation) {
+ if (executeActions(player, data.textVL, added, cc.textFreqNormActions, isMainThread)) {
+ cancel = true;
+ }
+ }
+ }
+ }
+ else if (cc.chatWarningCheck && time - data.chatWarningTime > cc.chatWarningTimeout && (100f * accumulated / cc.textFreqNormLevel > cc.chatWarningLevel || 100f * shortTermAccumulated / cc.textFreqShortTermLevel > cc.chatWarningLevel)) {
+ NCPAPIProvider.getNoCheatPlusAPI().sendMessageOnTick(player.getName(), ColorUtil.replaceColors(cc.chatWarningMessage));
data.chatWarningTime = time;
- }
- else {
+ }
+ else {
data.textVL *= 0.95;
if (cc.textAllowVLReset && normalScore < 2.0f * cc.textFreqNormWeight && shortTermScore < 2.0f * cc.textFreqShortTermWeight) {
- // Reset the VL.
+ // Reset the VL.
// TODO: maybe elaborate on resetting conditions (after some timeout just divide by two or so?).
data.textVL = 0.0;
}
}
- if (debug) {
- final List keys = new LinkedList(engMap.keySet());
- Collections.sort(keys);
- for (String key : keys) {
- Float s = engMap.get(key);
- if (s.floatValue() > 0.0f)
- debugParts.add(key + ":" + StringUtil.fdec3.format(s));
- }
- if (wEngine > 0.0f)
- debugParts.add("Engine score (" + (cc.textEngineMaximum?"max":"sum") + "): " + StringUtil.fdec3.format(wEngine));
-
- debugParts.add("Final score: " + StringUtil.fdec3.format(score));
- debugParts.add("Normal: min=" + StringUtil.fdec3.format(cc.textFreqNormMin) +", weight=" + StringUtil.fdec3.format(cc.textFreqNormWeight) + " => accumulated=" + StringUtil.fdec3.format(accumulated));
- debugParts.add("Short-term: min=" + StringUtil.fdec3.format(cc.textFreqShortTermMin) +", weight=" + StringUtil.fdec3.format(cc.textFreqShortTermWeight) + " => accumulated=" + StringUtil.fdec3.format(shortTermAccumulated));
- debugParts.add("vl: " + StringUtil.fdec3.format(data.textVL));
- LogUtil.scheduleLogInfo(debugParts, " | ");
- debugParts.clear();
- }
-
- lastGlobalMessage = data.chatLastMessage = lcMessage;
- lastGlobalTime = data.chatLastTime = time;
-
- return cancel;
- }
-
- @Override
+ if (debug) {
+ final List keys = new LinkedList(engMap.keySet());
+ Collections.sort(keys);
+ for (String key : keys) {
+ Float s = engMap.get(key);
+ if (s.floatValue() > 0.0f)
+ debugParts.add(key + ":" + StringUtil.fdec3.format(s));
+ }
+ if (wEngine > 0.0f)
+ debugParts.add("Engine score (" + (cc.textEngineMaximum?"max":"sum") + "): " + StringUtil.fdec3.format(wEngine));
+
+ debugParts.add("Final score: " + StringUtil.fdec3.format(score));
+ debugParts.add("Normal: min=" + StringUtil.fdec3.format(cc.textFreqNormMin) +", weight=" + StringUtil.fdec3.format(cc.textFreqNormWeight) + " => accumulated=" + StringUtil.fdec3.format(accumulated));
+ debugParts.add("Short-term: min=" + StringUtil.fdec3.format(cc.textFreqShortTermMin) +", weight=" + StringUtil.fdec3.format(cc.textFreqShortTermWeight) + " => accumulated=" + StringUtil.fdec3.format(shortTermAccumulated));
+ debugParts.add("vl: " + StringUtil.fdec3.format(data.textVL));
+ LogUtil.scheduleLogInfo(debugParts, " | ");
+ debugParts.clear();
+ }
+
+ lastGlobalMessage = data.chatLastMessage = lcMessage;
+ lastGlobalTime = data.chatLastTime = time;
+
+ return cancel;
+ }
+
+ @Override
protected Map getParameterMap(final ViolationData violationData) {
final Map parameters = super.getParameterMap(violationData);
parameters.put(ParameterName.IP, violationData.player.getAddress().toString().substring(1).split(":")[0]);