143 lines
4.6 KiB
Java
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;
|
|
}
|
|
}
|