mirror of
https://github.com/NoCheatPlus/NoCheatPlus.git
synced 2025-01-15 12:01:51 +01:00
Ensure ActionFRequency does reset if time ran backwards.
This commit is contained in:
parent
2f13529a29
commit
24120f306a
@ -7,225 +7,238 @@ package fr.neatmonster.nocheatplus.utilities;
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class ActionFrequency {
|
public class ActionFrequency {
|
||||||
|
|
||||||
/** Reference time for filling in. */
|
|
||||||
private long time = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Buckets to fill weights in, each represents an interval of durBucket duration,
|
|
||||||
* index 0 is the latest, highest index is the oldest.
|
|
||||||
* Weights will get filled into the next buckets with time passed.
|
|
||||||
*/
|
|
||||||
private final float[] buckets;
|
|
||||||
|
|
||||||
/** Duration in milliseconds that oe bucket covers. */
|
/** Reference time for filling in. */
|
||||||
private final long durBucket;
|
private long time = 0;
|
||||||
|
private long lastUpdate = 0;
|
||||||
public ActionFrequency(final int nBuckets, final long durBucket){
|
|
||||||
this.buckets = new float[nBuckets];
|
|
||||||
this.durBucket = durBucket;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update and add.
|
|
||||||
* @param now
|
|
||||||
* @param amount
|
|
||||||
*/
|
|
||||||
public final void add(final long now, final float amount){
|
|
||||||
update(now);
|
|
||||||
buckets[0] += amount;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unchecked addition of amount to the first bucket.
|
|
||||||
* @param amount
|
|
||||||
*/
|
|
||||||
public final void add(final float amount){
|
|
||||||
buckets[0] += amount;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update without adding, also updates time.
|
* Buckets to fill weights in, each represents an interval of durBucket duration,
|
||||||
* @param now
|
* index 0 is the latest, highest index is the oldest.
|
||||||
*/
|
* Weights will get filled into the next buckets with time passed.
|
||||||
public final void update(final long now) {
|
*/
|
||||||
final long diff = now - time;
|
private final float[] buckets;
|
||||||
if (diff < durBucket){
|
|
||||||
// No update (first bucket).
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (diff >= durBucket * buckets.length || diff < 0){
|
|
||||||
// Clear (beyond range).
|
|
||||||
clear(now);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final int shift = (int) ((float) diff / (float) durBucket);
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
// Set time according to bucket duration (!).
|
|
||||||
time += durBucket * shift;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void clear(final long now) {
|
/** Duration in milliseconds that oe bucket covers. */
|
||||||
for (int i = 0; i < buckets.length; i++){
|
private final long durBucket;
|
||||||
buckets[i] = 0f;
|
|
||||||
}
|
public ActionFrequency(final int nBuckets, final long durBucket) {
|
||||||
time = now;
|
this.buckets = new float[nBuckets];
|
||||||
}
|
this.durBucket = durBucket;
|
||||||
|
}
|
||||||
/**
|
|
||||||
* @deprecated Use instead: score(float).
|
/**
|
||||||
* @param factor
|
* Update and add.
|
||||||
* @return
|
* @param now
|
||||||
*/
|
* @param amount
|
||||||
public final float getScore(final float factor){
|
*/
|
||||||
return score(factor);
|
public final void add(final long now, final float amount) {
|
||||||
}
|
update(now);
|
||||||
|
buckets[0] += amount;
|
||||||
/**
|
}
|
||||||
* @deprecated Use instead: score(float).
|
|
||||||
* @param factor
|
/**
|
||||||
* @return
|
* Unchecked addition of amount to the first bucket.
|
||||||
*/
|
* @param amount
|
||||||
public final float getScore(final int bucket){
|
*/
|
||||||
return bucketScore(bucket);
|
public final void add(final float amount) {
|
||||||
}
|
buckets[0] += amount;
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Get a weighted sum score, weight for bucket i: w(i) = factor^i.
|
/**
|
||||||
* @param factor
|
* Update without adding, also updates time. Detects time running backwards.
|
||||||
* @return
|
* @param now
|
||||||
*/
|
*/
|
||||||
public final float score(final float factor){
|
public final void update(final long now) {
|
||||||
return sliceScore(0, buckets.length, factor);
|
final long diff = now - time;
|
||||||
}
|
if (now < lastUpdate || diff >= durBucket * buckets.length) {
|
||||||
|
// Clear (beyond range).
|
||||||
/**
|
clear(now);
|
||||||
* Get score of a certain bucket. At own risk.
|
return;
|
||||||
* @param bucket
|
}
|
||||||
* @return
|
else if (diff < durBucket) {
|
||||||
*/
|
// No update (first bucket).
|
||||||
public final float bucketScore(final int bucket){
|
return;
|
||||||
return buckets[bucket];
|
}
|
||||||
}
|
final int shift = (int) ((float) diff / (float) durBucket);
|
||||||
|
// Update buckets.
|
||||||
/**
|
for (int i = 0; i < buckets.length - shift; i++) {
|
||||||
* Get score of first end buckets, with factor.
|
buckets[buckets.length - (i + 1)] = buckets[buckets.length - (i + 1 + shift)];
|
||||||
* @param end Number of buckets including start. The end is not included.
|
}
|
||||||
* @param factor
|
for (int i = 0; i < shift; i++) {
|
||||||
* @return
|
buckets[i] = 0;
|
||||||
*/
|
}
|
||||||
public final float leadingScore(final int end, float factor){
|
// Set time according to bucket duration (!).
|
||||||
return sliceScore(0, end, factor);
|
time += durBucket * shift;
|
||||||
}
|
lastUpdate = now;
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Get score from start on, with factor.
|
public final void clear(final long now) {
|
||||||
* @param start This is included.
|
for (int i = 0; i < buckets.length; i++) {
|
||||||
* @param factor
|
buckets[i] = 0f;
|
||||||
* @return
|
}
|
||||||
*/
|
time = lastUpdate = now;
|
||||||
public final float trailingScore(final int start, float factor){
|
}
|
||||||
return sliceScore(start, buckets.length, factor);
|
|
||||||
}
|
/**
|
||||||
|
* @deprecated Use instead: score(float).
|
||||||
/**
|
* @param factor
|
||||||
* Get score from start on, until before end, with factor.
|
* @return
|
||||||
* @param start This is included.
|
*/
|
||||||
* @param end This is not included.
|
public final float getScore(final float factor) {
|
||||||
* @param factor
|
return score(factor);
|
||||||
* @return
|
}
|
||||||
*/
|
|
||||||
public final float sliceScore(final int start, final int end, float factor){
|
/**
|
||||||
float score = buckets[start];
|
* @deprecated Use instead: score(float).
|
||||||
float cf = factor;
|
* @param factor
|
||||||
for (int i = start + 1; i < end; i++){
|
* @return
|
||||||
score += buckets[i] * cf;
|
*/
|
||||||
cf *= factor;
|
public final float getScore(final int bucket) {
|
||||||
}
|
return bucketScore(bucket);
|
||||||
return score;
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
/**
|
* Get a weighted sum score, weight for bucket i: w(i) = factor^i.
|
||||||
* Set the value for a buckt.
|
* @param factor
|
||||||
* @param n
|
* @return
|
||||||
* @param value
|
*/
|
||||||
*/
|
public final float score(final float factor) {
|
||||||
public final void setBucket(final int n, final float value){
|
return sliceScore(0, buckets.length, factor);
|
||||||
buckets[n] = value;
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
/**
|
* Get score of a certain bucket. At own risk.
|
||||||
* Set the reference time.
|
* @param bucket
|
||||||
* @param time
|
* @return
|
||||||
*/
|
*/
|
||||||
public final void setTime(final long time){
|
public final float bucketScore(final int bucket) {
|
||||||
this.time = time;
|
return buckets[bucket];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get reference time.
|
* Get score of first end buckets, with factor.
|
||||||
* @return
|
* @param end Number of buckets including start. The end is not included.
|
||||||
*/
|
* @param factor
|
||||||
public final long lastAccess(){
|
* @return
|
||||||
return time;
|
*/
|
||||||
}
|
public final float leadingScore(final int end, float factor) {
|
||||||
|
return sliceScore(0, end, factor);
|
||||||
/**
|
}
|
||||||
* Get the number of buckets.
|
|
||||||
* @return
|
/**
|
||||||
*/
|
* Get score from start on, with factor.
|
||||||
public final int numberOfBuckets(){
|
* @param start This is included.
|
||||||
return buckets.length;
|
* @param factor
|
||||||
}
|
* @return
|
||||||
|
*/
|
||||||
/**
|
public final float trailingScore(final int start, float factor) {
|
||||||
* Get the duration of a bucket in milliseconds.
|
return sliceScore(start, buckets.length, factor);
|
||||||
* @return
|
}
|
||||||
*/
|
|
||||||
public final long bucketDuration(){
|
/**
|
||||||
return durBucket;
|
* Get score from start on, until before end, with factor.
|
||||||
}
|
* @param start This is included.
|
||||||
|
* @param end This is not included.
|
||||||
/**
|
* @param factor
|
||||||
* Serialize to a String line.
|
* @return
|
||||||
* @return
|
*/
|
||||||
*/
|
public final float sliceScore(final int start, final int end, float factor) {
|
||||||
public final String toLine(){
|
float score = buckets[start];
|
||||||
final StringBuilder buffer = new StringBuilder(50);
|
float cf = factor;
|
||||||
buffer.append(buckets.length + ","+durBucket+","+time);
|
for (int i = start + 1; i < end; i++) {
|
||||||
for (int i = 0; i < buckets.length; i++){
|
score += buckets[i] * cf;
|
||||||
buffer.append("," + buckets[i]);
|
cf *= factor;
|
||||||
}
|
}
|
||||||
return buffer.toString();
|
return score;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deserialize from a string.
|
* Set the value for a buckt.
|
||||||
* @param line
|
* @param n
|
||||||
* @return
|
* @param value
|
||||||
*/
|
*/
|
||||||
public static ActionFrequency fromLine(final String line){
|
public final void setBucket(final int n, final float value) {
|
||||||
String[] split = line.split(",");
|
buckets[n] = value;
|
||||||
if (split.length < 3) throw new RuntimeException("Bad argument length."); // TODO
|
}
|
||||||
final int n = Integer.parseInt(split[0]);
|
|
||||||
final long durBucket = Long.parseLong(split[1]);
|
/**
|
||||||
final long time = Long.parseLong(split[2]);
|
* Set the reference time.
|
||||||
final float[] buckets = new float[split.length -3];
|
* @param time
|
||||||
if (split.length - 3 != buckets.length) throw new RuntimeException("Bad argument length."); // TODO
|
*/
|
||||||
for (int i = 3; i < split.length; i ++){
|
public final void setTime(final long time) {
|
||||||
buckets[i - 3] = Float.parseFloat(split[i]);
|
this.time = time;
|
||||||
}
|
this.lastUpdate = time;
|
||||||
ActionFrequency freq = new ActionFrequency(n, durBucket);
|
}
|
||||||
freq.setTime(time);
|
|
||||||
for (int i = 0; i < buckets.length; i ++){
|
/**
|
||||||
freq.setBucket(i, buckets[i]);
|
* Get reference time.
|
||||||
}
|
* @return
|
||||||
return freq;
|
*/
|
||||||
}
|
public final long lastAccess() { // TODO: Should rename this.
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public final long lastUpdate() {
|
||||||
|
return lastUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the number of buckets.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public final int numberOfBuckets() {
|
||||||
|
return buckets.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the duration of a bucket in milliseconds.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public final long bucketDuration() {
|
||||||
|
return durBucket;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize to a String line.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public final String toLine() {
|
||||||
|
// TODO: Backwards-compatible lastUpdate ?
|
||||||
|
final StringBuilder buffer = new StringBuilder(50);
|
||||||
|
buffer.append(buckets.length + ","+durBucket+","+time);
|
||||||
|
for (int i = 0; i < buckets.length; i++) {
|
||||||
|
buffer.append("," + buckets[i]);
|
||||||
|
}
|
||||||
|
return buffer.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserialize from a string.
|
||||||
|
* @param line
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static ActionFrequency fromLine(final String line) {
|
||||||
|
// TODO: Backwards-compatible lastUpdate ?
|
||||||
|
String[] split = line.split(",");
|
||||||
|
if (split.length < 3) throw new RuntimeException("Bad argument length."); // TODO
|
||||||
|
final int n = Integer.parseInt(split[0]);
|
||||||
|
final long durBucket = Long.parseLong(split[1]);
|
||||||
|
final long time = Long.parseLong(split[2]);
|
||||||
|
final float[] buckets = new float[split.length -3];
|
||||||
|
if (split.length - 3 != buckets.length) throw new RuntimeException("Bad argument length."); // TODO
|
||||||
|
for (int i = 3; i < split.length; i ++) {
|
||||||
|
buckets[i - 3] = Float.parseFloat(split[i]);
|
||||||
|
}
|
||||||
|
ActionFrequency freq = new ActionFrequency(n, durBucket);
|
||||||
|
freq.setTime(time);
|
||||||
|
for (int i = 0; i < buckets.length; i ++) {
|
||||||
|
freq.setBucket(i, buckets[i]);
|
||||||
|
}
|
||||||
|
return freq;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user