shakey shakey

This commit is contained in:
Brian Neumann-Fopiano
2026-02-14 05:44:10 -05:00
parent 86ed3f0095
commit 7885762cd7
5 changed files with 290 additions and 2 deletions

View File

@@ -72,6 +72,8 @@ public class PregeneratorJob implements PregenListener {
private PregenRenderer renderer;
private int rgc = 0;
private String[] info;
private volatile double lastChunksPerSecond = 0D;
private volatile long lastChunksRemaining = 0L;
public PregeneratorJob(PregenTask task, PregeneratorMethod method, Engine engine) {
instance.updateAndGet(old -> {
@@ -146,6 +148,16 @@ public class PregeneratorJob implements PregenListener {
return inst.paused();
}
public static double chunksPerSecond() {
PregeneratorJob inst = instance.get();
return inst == null ? 0D : Math.max(0D, inst.lastChunksPerSecond);
}
public static long chunksRemaining() {
PregeneratorJob inst = instance.get();
return inst == null ? -1L : Math.max(0L, inst.lastChunksRemaining);
}
private static Color parseColor(String c) {
String v = (c.startsWith("#") ? c : "#" + c).trim();
try {
@@ -234,6 +246,9 @@ public class PregeneratorJob implements PregenListener {
@Override
public void onTick(double chunksPerSecond, double chunksPerMinute, double regionsPerMinute, double percent, long generated, long totalChunks, long chunksRemaining, long eta, long elapsed, String method, boolean cached) {
lastChunksPerSecond = chunksPerSecond;
lastChunksRemaining = chunksRemaining;
info = new String[]{
(paused() ? "PAUSED" : (saving ? "Saving... " : "Generating")) + " " + Form.f(generated) + " of " + Form.f(totalChunks) + " (" + Form.pc(percent, 0) + " Complete)",
"Speed: " + (cached ? "Cached " : "") + Form.f(chunksPerSecond, 0) + " Chunks/s, " + Form.f(regionsPerMinute, 1) + " Regions/m, " + Form.f(chunksPerMinute, 0) + " Chunks/m",

View File

@@ -221,6 +221,25 @@ public class LazyPregenerator extends Thread implements Listener {
return job != null && job.isPaused();
}
public static long remainingChunks() {
LazyPregenerator local = instance;
AtomicInteger generated = lazyGeneratedChunks;
if (local == null || generated == null) {
return -1L;
}
return Math.max(0L, local.lazyTotalChunks.get() - generated.get());
}
public static double chunksPerSecond() {
LazyPregenerator local = instance;
if (local == null) {
return 0D;
}
return Math.max(0D, local.chunksPerMinute.getAverage() / 60D);
}
public void shutdownInstance(World world) throws IOException {
Iris.info("LazyGen: " + C.IRIS + world.getName() + C.BLUE + " Shutting down..");
LazyPregenJob job = jobs.get(world.getName());
@@ -282,4 +301,3 @@ public class LazyPregenerator extends Thread implements Listener {
boolean paused = false;
}
}

View File

@@ -285,6 +285,25 @@ public class TurboPregenerator extends Thread implements Listener {
return job != null && job.isPaused();
}
public static long remainingChunks() {
TurboPregenerator local = instance;
AtomicInteger generated = turboGeneratedChunks;
if (local == null || generated == null) {
return -1L;
}
return Math.max(0L, local.turboTotalChunks.get() - generated.get());
}
public static double chunksPerSecond() {
TurboPregenerator local = instance;
if (local == null) {
return 0D;
}
return Math.max(0D, local.chunksPerSecond.getAverage());
}
public void shutdownInstance(World world) throws IOException {
Iris.info("turboGen: " + C.IRIS + world.getName() + C.BLUE + " Shutting down..");
TurboPregenJob job = jobs.get(world.getName());
@@ -338,4 +357,3 @@ public class TurboPregenerator extends Thread implements Listener {
boolean paused = false;
}
}

View File

@@ -66,6 +66,24 @@ public class IrisEngineSVC implements IrisService {
worlds.clear();
}
public int getQueuedTectonicPlateCount() {
return queuedTectonicPlates.get();
}
public double getAverageIdleDuration() {
double min = minIdleDuration.get();
double max = maxIdleDuration.get();
if (!Double.isFinite(min) || !Double.isFinite(max) || min < 0D || max < 0D) {
return 0D;
}
if (max < min) {
return max;
}
return (min + max) / 2D;
}
public void engineStatus(VolmitSender sender) {
long[] sizes = new long[4];
long[] count = new long[4];

View File

@@ -0,0 +1,219 @@
package art.arcane.iris.core.service;
import art.arcane.iris.Iris;
import art.arcane.iris.core.gui.PregeneratorJob;
import art.arcane.iris.core.pregenerator.LazyPregenerator;
import art.arcane.iris.core.pregenerator.TurboPregenerator;
import art.arcane.iris.util.plugin.IrisService;
import art.arcane.volmlib.integration.IntegrationHandshakeRequest;
import art.arcane.volmlib.integration.IntegrationHandshakeResponse;
import art.arcane.volmlib.integration.IntegrationHeartbeat;
import art.arcane.volmlib.integration.IntegrationMetricDescriptor;
import art.arcane.volmlib.integration.IntegrationMetricSample;
import art.arcane.volmlib.integration.IntegrationMetricSchema;
import art.arcane.volmlib.integration.IntegrationProtocolNegotiator;
import art.arcane.volmlib.integration.IntegrationProtocolVersion;
import art.arcane.volmlib.integration.IntegrationServiceContract;
import org.bukkit.Bukkit;
import org.bukkit.plugin.ServicePriority;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
public class IrisIntegrationService implements IrisService, IntegrationServiceContract {
private static final Set<IntegrationProtocolVersion> SUPPORTED_PROTOCOLS = Set.of(
new IntegrationProtocolVersion(1, 0),
new IntegrationProtocolVersion(1, 1)
);
private static final Set<String> CAPABILITIES = Set.of(
"handshake",
"heartbeat",
"metrics",
"iris-engine-metrics"
);
private volatile IntegrationProtocolVersion negotiatedProtocol = new IntegrationProtocolVersion(1, 1);
@Override
public void onEnable() {
Bukkit.getServicesManager().register(IntegrationServiceContract.class, this, Iris.instance, ServicePriority.Normal);
Iris.verbose("Integration provider registered for Iris");
}
@Override
public void onDisable() {
Bukkit.getServicesManager().unregister(IntegrationServiceContract.class, this);
}
@Override
public String pluginId() {
return "iris";
}
@Override
public String pluginVersion() {
return Iris.instance.getDescription().getVersion();
}
@Override
public Set<IntegrationProtocolVersion> supportedProtocols() {
return SUPPORTED_PROTOCOLS;
}
@Override
public Set<String> capabilities() {
return CAPABILITIES;
}
@Override
public Set<IntegrationMetricDescriptor> metricDescriptors() {
return IntegrationMetricSchema.descriptors().stream()
.filter(descriptor -> descriptor.key().startsWith("iris."))
.collect(java.util.stream.Collectors.toSet());
}
@Override
public IntegrationHandshakeResponse handshake(IntegrationHandshakeRequest request) {
long now = System.currentTimeMillis();
if (request == null) {
return new IntegrationHandshakeResponse(
pluginId(),
pluginVersion(),
false,
null,
SUPPORTED_PROTOCOLS,
CAPABILITIES,
"missing request",
now
);
}
Optional<IntegrationProtocolVersion> negotiated = IntegrationProtocolNegotiator.negotiate(
SUPPORTED_PROTOCOLS,
request.supportedProtocols()
);
if (negotiated.isEmpty()) {
return new IntegrationHandshakeResponse(
pluginId(),
pluginVersion(),
false,
null,
SUPPORTED_PROTOCOLS,
CAPABILITIES,
"no-common-protocol",
now
);
}
negotiatedProtocol = negotiated.get();
return new IntegrationHandshakeResponse(
pluginId(),
pluginVersion(),
true,
negotiatedProtocol,
SUPPORTED_PROTOCOLS,
CAPABILITIES,
"ok",
now
);
}
@Override
public IntegrationHeartbeat heartbeat() {
long now = System.currentTimeMillis();
return new IntegrationHeartbeat(negotiatedProtocol, true, now, "ok");
}
@Override
public Map<String, IntegrationMetricSample> sampleMetrics(Set<String> metricKeys) {
Set<String> requested = metricKeys == null || metricKeys.isEmpty()
? IntegrationMetricSchema.irisKeys()
: metricKeys;
long now = System.currentTimeMillis();
Map<String, IntegrationMetricSample> out = new HashMap<>();
for (String key : requested) {
switch (key) {
case IntegrationMetricSchema.IRIS_CHUNK_STREAM_MS -> out.put(key, sampleChunkStreamMetric(now));
case IntegrationMetricSchema.IRIS_PREGEN_QUEUE -> out.put(key, samplePregenQueueMetric(now));
case IntegrationMetricSchema.IRIS_BIOME_CACHE_HIT_RATE -> out.put(key, IntegrationMetricSample.unavailable(
IntegrationMetricSchema.descriptor(key),
"biome-cache-hit-rate-not-instrumented",
now
));
default -> out.put(key, IntegrationMetricSample.unavailable(
IntegrationMetricSchema.descriptor(key),
"unsupported-key",
now
));
}
}
return out;
}
private IntegrationMetricSample sampleChunkStreamMetric(long now) {
IntegrationMetricDescriptor descriptor = IntegrationMetricSchema.descriptor(IntegrationMetricSchema.IRIS_CHUNK_STREAM_MS);
double chunksPerSecond = PregeneratorJob.chunksPerSecond();
if (chunksPerSecond <= 0D) {
chunksPerSecond = TurboPregenerator.chunksPerSecond();
}
if (chunksPerSecond <= 0D) {
chunksPerSecond = LazyPregenerator.chunksPerSecond();
}
if (chunksPerSecond > 0D) {
return IntegrationMetricSample.available(descriptor, 1000D / chunksPerSecond, now);
}
IrisEngineSVC engineService = Iris.service(IrisEngineSVC.class);
if (engineService != null) {
double idle = engineService.getAverageIdleDuration();
if (idle > 0D && Double.isFinite(idle)) {
return IntegrationMetricSample.available(descriptor, idle, now);
}
}
return IntegrationMetricSample.unavailable(descriptor, "chunk-stream-not-active", now);
}
private IntegrationMetricSample samplePregenQueueMetric(long now) {
IntegrationMetricDescriptor descriptor = IntegrationMetricSchema.descriptor(IntegrationMetricSchema.IRIS_PREGEN_QUEUE);
long totalQueue = 0L;
boolean hasAnySource = false;
long pregenRemaining = PregeneratorJob.chunksRemaining();
if (pregenRemaining >= 0L) {
totalQueue += pregenRemaining;
hasAnySource = true;
}
long turboRemaining = TurboPregenerator.remainingChunks();
if (turboRemaining >= 0L) {
totalQueue += turboRemaining;
hasAnySource = true;
}
long lazyRemaining = LazyPregenerator.remainingChunks();
if (lazyRemaining >= 0L) {
totalQueue += lazyRemaining;
hasAnySource = true;
}
IrisEngineSVC engineService = Iris.service(IrisEngineSVC.class);
if (engineService != null) {
totalQueue += Math.max(0, engineService.getQueuedTectonicPlateCount());
hasAnySource = true;
}
if (!hasAnySource) {
return IntegrationMetricSample.unavailable(descriptor, "queue-not-available", now);
}
return IntegrationMetricSample.available(descriptor, totalQueue, now);
}
}