aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/gregtech/api/util/AveragePerTickCounter.java
blob: c9b1275deb946a1322bf50fbe0d23e7af3f75945 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
package gregtech.api.util;

import java.security.InvalidParameterException;
import java.util.ArrayDeque;

import net.minecraft.server.MinecraftServer;

public class AveragePerTickCounter {

    /**
     * Averages a value over a certain amount of ticks
     *
     * @param period amount of ticks to average (20 for 1 second)
     * 
     */
    public AveragePerTickCounter(int period) throws InvalidParameterException {

        if (period <= 0) throw new InvalidParameterException("period should be a positive non-zero number");

        this.period = period;
        values = new ArrayDeque<>(period);
    }

    public void addValue(long value) {

        if (value == 0) return;

        final int currTick = getWorldTimeInTicks();

        if (values.isEmpty()) {
            values.addLast(new Measurement(currTick, value));
            isCachedAverageValid = false;
            return;
        }

        Measurement lastMeasurement = values.peekLast();
        final int lastMeasurementTick = lastMeasurement.TimestampInWorldTicks;

        /// sums up values added in the same tick
        /// for example a cable had an amp running through it multiple times in the same tick
        if (currTick == lastMeasurementTick) {
            lastMeasurement.Value = lastMeasurement.Value + value;
            isCachedAverageValid = false;
            return;
        }

        if (currTick > lastMeasurementTick) {
            trimIrrelevantData(currTick);

            values.addLast(new Measurement(currTick, value));
            isCachedAverageValid = false;
            return;
        }
    }

    public double getAverage() {

        if (values.isEmpty()) return 0;

        final int currTick = getWorldTimeInTicks();

        Measurement lastMeasurement = values.peekLast();
        final int lastMeasurementTick = lastMeasurement.TimestampInWorldTicks;

        if (currTick < lastMeasurementTick) return 0;

        if (currTick > lastMeasurementTick) {
            trimIrrelevantData(currTick);
        }

        if (isCachedAverageValid) return cachedAverage;

        return calculateAverage();
    }

    public long getLast() {

        if (values.isEmpty()) return 0;

        final int currTick = getWorldTimeInTicks();

        Measurement lastMeasurement = values.peekLast();
        final int lastMeasurementTick = lastMeasurement.TimestampInWorldTicks;

        if (currTick == lastMeasurementTick) return values.getLast().Value;

        return 0;
    }

    private double calculateAverage() {

        isCachedAverageValid = true;
        long sum = 0;

        for (Measurement measurement : values) {
            sum += measurement.Value;
        }

        return sum / (double) period;
    }

    private void trimIrrelevantData(int currWorldTimeInTicks) {

        if (values.isEmpty()) return;

        int firstMeasurementTick = values.peekFirst().TimestampInWorldTicks;

        while (currWorldTimeInTicks - firstMeasurementTick >= period) {
            values.removeFirst();
            isCachedAverageValid = false;

            if (values.isEmpty()) return;

            firstMeasurementTick = values.peekFirst().TimestampInWorldTicks;
        }
    }

    private int getWorldTimeInTicks() {
        return MinecraftServer.getServer()
            .getTickCounter();
    }

    private ArrayDeque<Measurement> values;
    private int period;

    private double cachedAverage = 0;
    private boolean isCachedAverageValid = true;

    private class Measurement {

        public int TimestampInWorldTicks;
        public long Value;

        public Measurement(int timestampInWorldTicks, long value) {
            this.TimestampInWorldTicks = timestampInWorldTicks;
            this.Value = value;
        }
    }
}