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]);