Files
common/src/main/java/org/saturnclient/mod/mods/TpsMod.java
T

143 lines
4.6 KiB
Java

package org.saturnclient.mod.mods;
import org.saturnclient.ui.RenderScope;
import org.saturnclient.ui.resources.Fonts;
import org.saturnclient.config.property.BoolProperty;
import org.saturnclient.config.property.Property;
import org.saturnclient.mod.HudMod;
import org.saturnclient.mod.Mod;
import org.saturnclient.mod.ModLayout;
import org.saturnclient.mod.ModSpec;
/**
* TpsMod estimates the server's ticks-per-second by measuring the
* elapsed real time between consecutive world-time packets.
*
* TPS calculation is event-driven: the networking layer (or mixin)
* calls {@link #onTimePacket(long)} whenever a time-update packet
* arrives. No ModuleProvider is required because there is no
* per-frame polling of the engine.
*/
public class TpsMod extends Mod implements HudMod {
private static final BoolProperty enabled = Property.bool(false);
private static final ModLayout layout = new ModLayout(60, Fonts.getHeight());
// ---------------------------------------------------------------
// TPS sampling state (static — shared across all instances)
// ---------------------------------------------------------------
private static final int SAMPLE_COUNT = 2;
private static final double[] samples = new double[SAMPLE_COUNT];
static {
java.util.Arrays.fill(samples, 20.0);
}
private static long lastWorldAge = -1L;
private static long lastRealTime = -1L;
private static double currentTps = 20.0;
private static int sampleIndex = 0;
private static int samplesFilled = SAMPLE_COUNT;
// ---------------------------------------------------------------
// Constructor
// ---------------------------------------------------------------
public TpsMod() {
super(
new ModSpec("TPS Display", "tps")
.description("Displays the server's ticks per second")
.version("v0.2.0")
.tags("Utility"),
enabled.named("Enabled"),
layout.prop());
}
// ---------------------------------------------------------------
// Packet hook (called by the networking mixin)
// ---------------------------------------------------------------
/**
* Must be called every time the client receives a world-time packet
* carrying the updated {@code worldAge} value.
*/
public static void onTimePacket(long worldAge) {
long now = System.currentTimeMillis();
if (lastWorldAge != -1L && lastRealTime != -1L) {
long tickDelta = worldAge - lastWorldAge;
long timeDelta = now - lastRealTime;
if (timeDelta > 0 && tickDelta > 0) {
double sample = Math.min((tickDelta / (double) timeDelta) * 1000.0, 20.0);
samples[sampleIndex % SAMPLE_COUNT] = sample;
sampleIndex++;
samplesFilled = Math.min(samplesFilled + 1, SAMPLE_COUNT);
double sum = 0;
for (int i = 0; i < samplesFilled; i++)
sum += samples[i];
currentTps = sum / samplesFilled;
}
}
lastWorldAge = worldAge;
lastRealTime = now;
}
/** Resets TPS history (e.g. on server disconnect). */
public static void reset() {
lastWorldAge = -1L;
lastRealTime = -1L;
currentTps = 20.0;
sampleIndex = 0;
samplesFilled = SAMPLE_COUNT;
java.util.Arrays.fill(samples, 20.0);
}
// ---------------------------------------------------------------
// HudMod
// ---------------------------------------------------------------
@Override
public void renderHud(RenderScope scope) {
renderTps(currentTps, scope);
}
@Override
public void renderDummy(RenderScope scope) {
renderTps(20.0, scope);
}
// ---------------------------------------------------------------
// Rendering
// ---------------------------------------------------------------
private void renderTps(double tps, RenderScope scope) {
String text = String.format("%.1f TPS", tps);
scope.drawText(text, 0, 0, layout.font.value, layout.fgColor.value);
layout.width = Fonts.getWidth(text, layout.font.value);
}
// ---------------------------------------------------------------
// Mod contract
// ---------------------------------------------------------------
@Override
public ModLayout getDimensions() {
return layout;
}
@Override
public boolean isEnabled() {
return enabled.value;
}
@Override
public void onEnabled(boolean e) {
enabled.value = e;
}
}