mirror of
https://github.com/NoCheatPlus/NoCheatPlus.git
synced 2024-11-02 08:40:01 +01:00
Actually it is CompressedWords, rather.
This commit is contained in:
parent
17ded8adee
commit
f2b29da6a6
@ -18,7 +18,7 @@ public class CharPrefixTree<N extends CharNode, L extends CharLookupEntry<N>> ex
|
||||
}
|
||||
}
|
||||
|
||||
public CharPrefixTree(NodeFactory<Character, N> nodeFactory, LookupEntryFactory<Character, N, L> resultFactory) {
|
||||
public CharPrefixTree(final NodeFactory<Character, N> nodeFactory, final LookupEntryFactory<Character, N, L> resultFactory) {
|
||||
super(nodeFactory, resultFactory);
|
||||
}
|
||||
|
||||
@ -41,7 +41,7 @@ public class CharPrefixTree<N extends CharNode, L extends CharLookupEntry<N>> ex
|
||||
* @param create
|
||||
* @return
|
||||
*/
|
||||
public L lookup(final char[] chars, boolean create){
|
||||
public L lookup(final char[] chars, final boolean create){
|
||||
return lookup(toCharacterList(chars), create);
|
||||
}
|
||||
|
||||
@ -51,7 +51,7 @@ public class CharPrefixTree<N extends CharNode, L extends CharLookupEntry<N>> ex
|
||||
* @param create
|
||||
* @return
|
||||
*/
|
||||
public L lookup(final String input, boolean create){
|
||||
public L lookup(final String input, final boolean create){
|
||||
return lookup(input.toCharArray(), create);
|
||||
}
|
||||
|
||||
@ -73,7 +73,7 @@ public class CharPrefixTree<N extends CharNode, L extends CharLookupEntry<N>> ex
|
||||
return feed(toCharacterList(chars));
|
||||
}
|
||||
|
||||
public void feedAll(final Collection<String> inputs, boolean trim, boolean lowerCase){
|
||||
public void feedAll(final Collection<String> inputs, final boolean trim, final boolean lowerCase){
|
||||
for (String input : inputs){
|
||||
if (trim) input = input.toLowerCase();
|
||||
if (lowerCase) input = input.toLowerCase();
|
||||
@ -118,7 +118,7 @@ public class CharPrefixTree<N extends CharNode, L extends CharLookupEntry<N>> ex
|
||||
}
|
||||
}, new LookupEntryFactory<Character, CharNode, CharLookupEntry<CharNode>>() {
|
||||
@Override
|
||||
public CharLookupEntry<CharNode> newLookupEntry(CharNode node, CharNode insertion, int depth, boolean hasPrefix) {
|
||||
public final CharLookupEntry<CharNode> newLookupEntry(final CharNode node, final CharNode insertion, final int depth, final boolean hasPrefix) {
|
||||
return new CharLookupEntry<CharNode>(node, insertion, depth, hasPrefix);
|
||||
}
|
||||
});
|
||||
|
@ -83,7 +83,7 @@ public class PrefixTree<K, N extends Node<K>, L extends LookupEntry<K, N>>{
|
||||
/** If nodes visit method shall be called. */
|
||||
protected boolean visit;
|
||||
|
||||
public PrefixTree(NodeFactory<K, N> nodeFactory, LookupEntryFactory<K, N, L> resultFactory){
|
||||
public PrefixTree(final NodeFactory<K, N> nodeFactory, final LookupEntryFactory<K, N, L> resultFactory){
|
||||
this.nodeFactory = nodeFactory;
|
||||
this.root = nodeFactory.newNode(null);
|
||||
this.resultFactory = resultFactory;
|
||||
@ -113,7 +113,7 @@ public class PrefixTree<K, N extends Node<K>, L extends LookupEntry<K, N>>{
|
||||
* @param create
|
||||
* @return
|
||||
*/
|
||||
public L lookup(K[] keys, final boolean create){
|
||||
public L lookup(final K[] keys, final boolean create){
|
||||
return lookup(Arrays.asList(keys), create);
|
||||
}
|
||||
|
||||
@ -156,7 +156,9 @@ public class PrefixTree<K, N extends Node<K>, L extends LookupEntry<K, N>>{
|
||||
else if (depth == keys.size()){
|
||||
node = current;
|
||||
}
|
||||
return resultFactory.newLookupEntry(node, insertion, depth, hasPrefix);
|
||||
final L result = resultFactory.newLookupEntry(node, insertion, depth, hasPrefix);
|
||||
decorate(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -166,6 +168,13 @@ public class PrefixTree<K, N extends Node<K>, L extends LookupEntry<K, N>>{
|
||||
protected void visit(final N node){
|
||||
}
|
||||
|
||||
/**
|
||||
* Decorate before returning.
|
||||
* @param result
|
||||
*/
|
||||
protected void decorate(final L result){
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param keys
|
||||
@ -258,7 +267,7 @@ public class PrefixTree<K, N extends Node<K>, L extends LookupEntry<K, N>>{
|
||||
}
|
||||
}, new LookupEntryFactory<K, Node<K>, LookupEntry<K,Node<K>>>() {
|
||||
@Override
|
||||
public LookupEntry<K, Node<K>> newLookupEntry(Node<K> node, Node<K> insertion, int depth, boolean hasPrefix) {
|
||||
public final LookupEntry<K, Node<K>> newLookupEntry(final Node<K> node, final Node<K> insertion, final int depth, final boolean hasPrefix) {
|
||||
return new LookupEntry<K, PrefixTree.Node<K>>(node, insertion, depth, hasPrefix);
|
||||
}
|
||||
});
|
||||
|
@ -25,7 +25,7 @@ public class SimpleCharPrefixTree extends CharPrefixTree<CharNode, SimpleCharLoo
|
||||
}
|
||||
}, new LookupEntryFactory<Character, CharNode, SimpleCharLookupEntry>() {
|
||||
@Override
|
||||
public SimpleCharLookupEntry newLookupEntry(CharNode node, CharNode insertion, int depth, boolean hasPrefix) {
|
||||
public final SimpleCharLookupEntry newLookupEntry(final CharNode node, final CharNode insertion, final int depth, final boolean hasPrefix) {
|
||||
return new SimpleCharLookupEntry(node, insertion, depth, hasPrefix);
|
||||
}
|
||||
});
|
||||
|
@ -0,0 +1,37 @@
|
||||
package fr.neatmonster.nocheatplus.checks.chat.analysis.ds;
|
||||
|
||||
import fr.neatmonster.nocheatplus.checks.chat.analysis.ds.SimpleTimedCharPrefixTree.SimpleTimedCharLookupEntry;
|
||||
import fr.neatmonster.nocheatplus.checks.chat.analysis.ds.TimedCharPrefixTree.TimedCharLookupEntry;
|
||||
import fr.neatmonster.nocheatplus.checks.chat.analysis.ds.TimedCharPrefixTree.TimedCharNode;
|
||||
|
||||
public class SimpleTimedCharPrefixTree extends TimedCharPrefixTree<TimedCharNode, SimpleTimedCharLookupEntry> {
|
||||
|
||||
public static class SimpleTimedCharLookupEntry extends TimedCharLookupEntry<TimedCharNode>{
|
||||
public SimpleTimedCharLookupEntry(TimedCharNode node, TimedCharNode insertion, int depth, boolean hasPrefix) {
|
||||
super(node, insertion, depth, hasPrefix);
|
||||
}
|
||||
}
|
||||
|
||||
public SimpleTimedCharPrefixTree(final boolean access){
|
||||
super(
|
||||
new NodeFactory<Character, TimedCharNode>(){
|
||||
@Override
|
||||
public final TimedCharNode newNode(final TimedCharNode parent) {
|
||||
final long ts;
|
||||
if (parent == null) ts = System.currentTimeMillis();
|
||||
else ts = parent.ts;
|
||||
return new TimedCharNode(ts);
|
||||
}
|
||||
}
|
||||
,
|
||||
new LookupEntryFactory<Character, TimedCharNode, SimpleTimedCharLookupEntry>() {
|
||||
@Override
|
||||
public final SimpleTimedCharLookupEntry newLookupEntry(final TimedCharNode node,
|
||||
final TimedCharNode insertion, final int depth, final boolean hasPrefix) {
|
||||
return new SimpleTimedCharLookupEntry(node, insertion, depth, hasPrefix);
|
||||
}
|
||||
},
|
||||
access);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
package fr.neatmonster.nocheatplus.checks.chat.analysis.ds;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import fr.neatmonster.nocheatplus.checks.chat.analysis.ds.TimedCharPrefixTree.TimedCharLookupEntry;
|
||||
import fr.neatmonster.nocheatplus.checks.chat.analysis.ds.TimedCharPrefixTree.TimedCharNode;
|
||||
|
||||
public class TimedCharPrefixTree<N extends TimedCharNode, L extends TimedCharLookupEntry<N>> extends CharPrefixTree<N, L> {
|
||||
|
||||
public static class TimedCharLookupEntry<N extends TimedCharNode> extends CharLookupEntry<N>{
|
||||
public long[] timeInsertion = null;
|
||||
public TimedCharLookupEntry(N node, N insertion, int depth, boolean hasPrefix){
|
||||
super(node, insertion, depth, hasPrefix);
|
||||
}
|
||||
}
|
||||
|
||||
public static class TimedCharNode extends CharNode{
|
||||
public long ts = 0;
|
||||
public TimedCharNode(long ts){
|
||||
this.ts = ts;
|
||||
}
|
||||
}
|
||||
|
||||
protected long ts;
|
||||
|
||||
protected long[] timeInsertion = new long[200];
|
||||
|
||||
protected final boolean access;
|
||||
|
||||
protected boolean updateTime = false;
|
||||
|
||||
protected int depth;
|
||||
|
||||
protected float arrayGrowth = 1.3f;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param nodeFactory
|
||||
* @param resultFactory
|
||||
* @param access If to set timestamps, even if create is false.
|
||||
*/
|
||||
public TimedCharPrefixTree(final NodeFactory<Character, N> nodeFactory, final LookupEntryFactory<Character, N, L> resultFactory, final boolean access) {
|
||||
super(nodeFactory, resultFactory);
|
||||
visit = true;
|
||||
this.access = access;
|
||||
}
|
||||
|
||||
@Override
|
||||
public L lookup(final List<Character> keys, final boolean create) {
|
||||
ts = System.currentTimeMillis();
|
||||
updateTime = access || create;
|
||||
depth = 0;
|
||||
return super.lookup(keys, create);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void visit(final N node) {
|
||||
if (depth == timeInsertion.length){
|
||||
// This might be excluded by contract.
|
||||
timeInsertion = Arrays.copyOf(timeInsertion, (int) (timeInsertion.length * arrayGrowth));
|
||||
}
|
||||
timeInsertion[depth] = node.ts;
|
||||
if (updateTime) node.ts = ts;
|
||||
depth ++;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void decorate(final L result) {
|
||||
result.timeInsertion = Arrays.copyOf(timeInsertion, depth);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param access If to set timestamps, even if create is false.
|
||||
* @return
|
||||
*/
|
||||
public static TimedCharPrefixTree<TimedCharNode, TimedCharLookupEntry<TimedCharNode>> newTimedCharPrefixTree(final boolean access) {
|
||||
return new TimedCharPrefixTree<TimedCharNode, TimedCharLookupEntry<TimedCharNode>>(
|
||||
new NodeFactory<Character, TimedCharNode>(){
|
||||
@Override
|
||||
public final TimedCharNode newNode(final TimedCharNode parent) {
|
||||
final long ts;
|
||||
if (parent == null) ts = System.currentTimeMillis();
|
||||
else ts = parent.ts;
|
||||
return new TimedCharNode(ts);
|
||||
}
|
||||
}
|
||||
,
|
||||
new LookupEntryFactory<Character, TimedCharNode, TimedCharLookupEntry<TimedCharNode>>() {
|
||||
@Override
|
||||
public final TimedCharLookupEntry<TimedCharNode> newLookupEntry(final TimedCharNode node,
|
||||
final TimedCharNode insertion, final int depth, final boolean hasPrefix) {
|
||||
return new TimedCharLookupEntry<TimedCharNode>(node, insertion, depth, hasPrefix);
|
||||
}
|
||||
},
|
||||
access
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
package fr.neatmonster.nocheatplus.checks.chat.analysis.engine;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import fr.neatmonster.nocheatplus.checks.chat.analysis.MessageLetterCount;
|
||||
import fr.neatmonster.nocheatplus.checks.chat.analysis.WordLetterCount;
|
||||
import fr.neatmonster.nocheatplus.checks.chat.analysis.ds.SimplePrefixTree;
|
||||
import fr.neatmonster.nocheatplus.checks.chat.analysis.ds.SimplePrefixTree.SimpleLookupEntry;
|
||||
|
||||
public class CompressedChars extends AbstractWordProcessor{
|
||||
|
||||
protected final SimplePrefixTree<Character> tree = new SimplePrefixTree<Character>();
|
||||
|
||||
protected final int maxAdd;
|
||||
|
||||
protected int added = 0;
|
||||
|
||||
private final boolean sort;
|
||||
|
||||
public CompressedChars(int maxAdd, boolean sort) {
|
||||
super("CompressedChars");
|
||||
this.maxAdd = maxAdd;
|
||||
this.sort = sort;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(MessageLetterCount message) {
|
||||
// This allows adding up to maximum messge length more characters,
|
||||
// but also allows to set size of nodes exactly.
|
||||
// TODO: Some better method than blunt clear (extra LinkedHashSet/LRU?).
|
||||
if (added > maxAdd) tree.clear();
|
||||
added = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float loop(long ts, int index, String key, WordLetterCount word) {
|
||||
final int len = word.counts.size();
|
||||
final List<Character> letters = new ArrayList<Character>(len);
|
||||
final List<Character> numbers = new ArrayList<Character>(Math.min(len, 10));
|
||||
final List<Character> other = new ArrayList<Character>(Math.min(len, 10));
|
||||
for (Character c : word.counts.keySet()){
|
||||
if (Character.isLetter(c)) letters.add(c);
|
||||
else if (Character.isDigit(c)) numbers.add(c);
|
||||
else other.add(c);
|
||||
}
|
||||
if (sort){
|
||||
Collections.sort(letters);
|
||||
Collections.sort(numbers);
|
||||
Collections.sort(other);
|
||||
}
|
||||
float score = 0;
|
||||
if (!letters.isEmpty()){
|
||||
score += getScore(letters);
|
||||
}
|
||||
if (!numbers.isEmpty()){
|
||||
score += getScore(numbers);
|
||||
}
|
||||
if (!other.isEmpty()){
|
||||
score += getScore(other);
|
||||
}
|
||||
return word.counts.isEmpty()?0f:(score / (float) len);
|
||||
}
|
||||
|
||||
private float getScore(List<Character> chars) {
|
||||
final int len = chars.size();
|
||||
SimpleLookupEntry<Character> entry = tree.lookup(chars, true);
|
||||
float score = (float) (entry.depth*entry.depth) / (float) (len*len) ;
|
||||
if (entry.depth == chars.size()){
|
||||
score += 0.2;
|
||||
if (entry.insertion.isEnd) score += 0.2;
|
||||
}
|
||||
if (len != entry.depth) added += len - entry.depth;
|
||||
return score;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
package fr.neatmonster.nocheatplus.checks.chat.analysis.engine;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import fr.neatmonster.nocheatplus.checks.chat.analysis.MessageLetterCount;
|
||||
import fr.neatmonster.nocheatplus.checks.chat.analysis.WordLetterCount;
|
||||
import fr.neatmonster.nocheatplus.checks.chat.analysis.ds.SimpleTimedCharPrefixTree;
|
||||
import fr.neatmonster.nocheatplus.checks.chat.analysis.ds.SimpleTimedCharPrefixTree.SimpleTimedCharLookupEntry;
|
||||
|
||||
public class CompressedWords extends AbstractWordProcessor{
|
||||
|
||||
protected final SimpleTimedCharPrefixTree tree = new SimpleTimedCharPrefixTree(true);
|
||||
|
||||
protected final int maxAdd;
|
||||
|
||||
protected int added = 0;
|
||||
|
||||
protected final boolean sort;
|
||||
|
||||
protected final long durExpire;
|
||||
|
||||
protected final List<Character> letters = new ArrayList<Character>(10);
|
||||
protected final List<Character> digits = new ArrayList<Character>(10);
|
||||
protected final List<Character> other = new ArrayList<Character>(10);
|
||||
|
||||
public CompressedWords(long durExpire, int maxAdd, boolean sort) {
|
||||
super("CompressedWords");
|
||||
this.durExpire = durExpire;
|
||||
this.maxAdd = maxAdd;
|
||||
this.sort = sort;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(MessageLetterCount message) {
|
||||
// This allows adding up to maximum messge length more characters,
|
||||
// but also allows to set size of nodes exactly.
|
||||
// TODO: Some better method than blunt clear (extra LinkedHashSet/LRU?).
|
||||
if (added > maxAdd) tree.clear();
|
||||
added = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float loop(final long ts, final int index, final String key, final WordLetterCount word) {
|
||||
final int len = word.counts.size();
|
||||
letters.clear();
|
||||
digits.clear();
|
||||
other.clear();
|
||||
for (Character c : word.counts.keySet()){
|
||||
if (Character.isLetter(c)) letters.add(c);
|
||||
else if (Character.isDigit(c)) digits.add(c);
|
||||
else other.add(c);
|
||||
}
|
||||
if (sort){
|
||||
Collections.sort(letters);
|
||||
Collections.sort(digits);
|
||||
Collections.sort(other);
|
||||
}
|
||||
float score = 0;
|
||||
if (!letters.isEmpty()){
|
||||
score += getScore(letters, ts);
|
||||
}
|
||||
if (!digits.isEmpty()){
|
||||
score += getScore(digits, ts);
|
||||
}
|
||||
if (!other.isEmpty()){
|
||||
score += getScore(other, ts);
|
||||
}
|
||||
return word.counts.isEmpty()?0f:(score / (float) len);
|
||||
}
|
||||
|
||||
protected float getScore(final List<Character> chars, final long ts) {
|
||||
final int len = chars.size();
|
||||
final SimpleTimedCharLookupEntry entry = tree.lookup(chars, true);
|
||||
final int depth = entry.depth;
|
||||
float score = 0f;
|
||||
for (int i = 0; i < depth ; i++){
|
||||
final long age = ts - entry.timeInsertion[i];
|
||||
if (age < durExpire)
|
||||
score += 1f / (float) (depth - i) * (float) (durExpire - age) / (float) durExpire;
|
||||
}
|
||||
if (depth == len){
|
||||
score += 0.2;
|
||||
if (entry.insertion.isEnd) score += 0.2;
|
||||
}
|
||||
if (len != depth) added += len - depth;
|
||||
return score;
|
||||
}
|
||||
|
||||
}
|
@ -25,7 +25,7 @@ public class LetterEngine {
|
||||
}
|
||||
if (config.getBoolean(ConfPaths.CHAT_GLOBALCHAT_ENGINE_GLCOMPRWORDS_CHECK, false)){
|
||||
// TODO: Make aspects configurable.
|
||||
processors.add(new CompressedChars(2000, false));
|
||||
processors.add(new CompressedWords(30000, 2000, false));
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user