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 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; } } }