Search in sources :

Example 1 with CombinedData

use of fr.neatmonster.nocheatplus.checks.combined.CombinedData in project NoCheatPlus by NoCheatPlus.

the class Text method unsafeCheck.

/**
 * 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, final IPlayerData pData, boolean isMainThread, final boolean alreadyCancelled) {
    // TODO: Skip captcha for "handleaschat" commands? [controversial potential]
    if (captcha.shouldCheckCaptcha(player, cc, data, pData)) {
        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;
    final boolean debug = pData.isDebugActive(type);
    final List<String> debugParts;
    if (debug) {
        debugParts = new LinkedList<String>();
        debugParts.add("Message (length=" + 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();
    // Upper case.
    if (letterCounts.fullCount.upperCase > msgLen / 3) {
        // TODO: Regard chunks of 48 or so letters of the message for this?
        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 !
        /*
             * TODO: 128 is a quick attempt to make one long message possible on
             * 1.11.
             */
        final float wRepetition = (float) Math.min(msgLen, 128) / 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 = pData.getGenericInstance(CombinedData.class);
    // TODO: maybe set dynamically in data.
    final long timeout = 8000;
    // 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;
            score += cc.textMsgRepeatSelf * timeWeight;
        }
    }
    // Repetition of last global message.
    if (cc.textMsgRepeatGlobal != 0f && time - lastGlobalTime < timeout) {
        if (StringUtil.isSimilar(lcMessage, lastGlobalMessage, 0.8f)) {
            final float timeWeight = (float) (timeout - (time - lastGlobalTime)) / (float) timeout;
            score += cc.textMsgRepeatGlobal * timeWeight;
        }
    }
    // 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.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;
        // TODO: quadratic ? (configurable)
        wWord *= wWord;
        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<String, Float> engMap;
    synchronized (engine) {
        engMap = engine.process(letterCounts, player.getName(), cc, data);
        // 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(player, cc, data, pData)) {
            captcha.sendNewCaptcha(player, cc, data);
            cancel = true;
        } else {
            if (shortTermViolation) {
                if (executeActions(player, data.textVL, added, cc.textFreqShortTermActions).willCancel()) {
                    cancel = true;
                }
            } else if (normalViolation) {
                if (executeActions(player, data.textVL, added, cc.textFreqNormActions).willCancel()) {
                    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 {
        data.textVL *= 0.95;
        if (cc.textAllowVLReset && normalScore < 2.0f * cc.textFreqNormWeight && shortTermScore < 2.0f * cc.textFreqShortTermWeight) {
            // 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<String> keys = new LinkedList<String>(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));
        debug(player, StringUtil.join(debugParts, " | "));
        debugParts.clear();
    }
    lastGlobalMessage = data.chatLastMessage = lcMessage;
    lastGlobalTime = data.chatLastTime = time;
    return cancel;
}
Also used : CombinedData(fr.neatmonster.nocheatplus.checks.combined.CombinedData) LinkedList(java.util.LinkedList) WordLetterCount(fr.neatmonster.nocheatplus.checks.chat.analysis.WordLetterCount) MessageLetterCount(fr.neatmonster.nocheatplus.checks.chat.analysis.MessageLetterCount)

Example 2 with CombinedData

use of fr.neatmonster.nocheatplus.checks.combined.CombinedData in project NoCheatPlus by NoCheatPlus.

the class MovingListener method onPlayerMoveMonitor.

/**
 * Monitor level PlayerMoveEvent. Uses useLoc.
 * @param event
 */
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = false)
public void onPlayerMoveMonitor(final PlayerMoveEvent event) {
    // TODO: Use stored move data to verify if from/to have changed (thus a teleport will result, possibly a minor issue due to the teleport).
    final long now = System.currentTimeMillis();
    final Player player = event.getPlayer();
    // TODO: Consider to store event.getFrom() from LOWEST priority in processingEvents.
    if (processingEvents.remove(player.getName()) == null) {
        // TODO: vehicles, cancelled, ...
        return;
    }
    if (player.isDead() || player.isSleeping()) {
        return;
    }
    final IPlayerData pData = DataManager.getPlayerData(event.getPlayer());
    // Feed combined check.
    final CombinedData data = pData.getGenericInstance(CombinedData.class);
    // TODO: Move to MovingData ?
    data.lastMoveTime = now;
    final Location from = event.getFrom();
    // Feed yawrate and reset moving data positions if necessary.
    final MovingData mData = pData.getGenericInstance(MovingData.class);
    final int tick = TickTask.getTick();
    final MovingConfig mCc = pData.getGenericInstance(MovingConfig.class);
    if (!event.isCancelled()) {
        final Location pLoc = player.getLocation(useLoc);
        onMoveMonitorNotCancelled(player, TrigUtil.isSamePosAndLook(pLoc, from) ? from : pLoc, event.getTo(), now, tick, data, mData, mCc, pData);
        useLoc.setWorld(null);
    } else {
        onCancelledMove(player, from, tick, now, mData, mCc, data, pData);
    }
}
Also used : Player(org.bukkit.entity.Player) IPlayerData(fr.neatmonster.nocheatplus.players.IPlayerData) CombinedData(fr.neatmonster.nocheatplus.checks.combined.CombinedData) PlayerLocation(fr.neatmonster.nocheatplus.utilities.location.PlayerLocation) Location(org.bukkit.Location) EventHandler(org.bukkit.event.EventHandler)

Example 3 with CombinedData

use of fr.neatmonster.nocheatplus.checks.combined.CombinedData in project NoCheatPlus by NoCheatPlus.

the class Relog method unsafeLoginCheck.

/**
 * Check (Join), only call from synchronized code.
 *
 * @param player
 *            the player
 * @param cc
 *            the cc
 * @param data
 *            the data
 * @return true, if successful
 */
public boolean unsafeLoginCheck(final Player player, final ChatConfig cc, final ChatData data, final IPlayerData pData) {
    boolean cancel = false;
    final long now = System.currentTimeMillis();
    final CombinedData cData = pData.getGenericInstance(CombinedData.class);
    // Enforce the player does not relog too fast.
    if (now - cData.lastLogoutTime < cc.relogTimeout) {
        if (now - data.relogWarningTime > cc.relogWarningTimeout)
            data.relogWarnings = 0;
        if (data.relogWarnings < cc.relogWarningNumber) {
            player.sendMessage(ColorUtil.replaceColors(cc.relogWarningMessage));
            data.relogWarningTime = now;
            data.relogWarnings++;
        } else {
            // Find out if we need to ban the player or not.
            data.relogVL += 1D;
            cancel = executeActions(player, (double) data.relogVL, 1D, cc.relogActions).willCancel();
        }
    }
    return cancel;
}
Also used : CombinedData(fr.neatmonster.nocheatplus.checks.combined.CombinedData)

Aggregations

CombinedData (fr.neatmonster.nocheatplus.checks.combined.CombinedData)3 MessageLetterCount (fr.neatmonster.nocheatplus.checks.chat.analysis.MessageLetterCount)1 WordLetterCount (fr.neatmonster.nocheatplus.checks.chat.analysis.WordLetterCount)1 IPlayerData (fr.neatmonster.nocheatplus.players.IPlayerData)1 PlayerLocation (fr.neatmonster.nocheatplus.utilities.location.PlayerLocation)1 LinkedList (java.util.LinkedList)1 Location (org.bukkit.Location)1 Player (org.bukkit.entity.Player)1 EventHandler (org.bukkit.event.EventHandler)1