mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-26 05:50:39 +00:00
Probe history digest
- Adds an opt-in digest of semantic and saved biome records per chunk
This commit is contained in:
1 parent
f7f9f5f651
commit
18ba6e32ac
5 files changed
+111
-5
No files matched your search
@@ -180,6 +180,7 @@ tasks.register('genProbe', JavaExec) {
|
||||
systemProperty('iris.probe.pregenProfile', providers.gradleProperty('probePregenProfile').orElse('false').get())
|
||||
systemProperty('iris.probe.hydrologyPreplan', providers.gradleProperty('probeHydrologyPreplan').orElse('false').get())
|
||||
systemProperty('iris.probe.historyRouter', providers.gradleProperty('probeHistoryRouter').orElse('false').get())
|
||||
systemProperty('iris.probe.historyDigest', providers.gradleProperty('probeHistoryDigest').orElse('false').get())
|
||||
systemProperty('iris.probe.productionBuffers', providers.gradleProperty('probeProductionBuffers').orElse('false').get())
|
||||
String probeCounters = providers.gradleProperty('probeCounters').orElse('false').get()
|
||||
systemProperty('iris.generation.counters', probeCounters)
|
||||
|
||||
@@ -177,7 +177,7 @@ public final class GenerationProbe {
|
||||
}
|
||||
|
||||
record ProbeOptions(int threads, Path dumpFile, boolean pregenProfile, boolean hydrologyPreplan,
|
||||
boolean historyRouter, boolean productionBuffers, boolean counters) {
|
||||
boolean historyRouter, boolean productionBuffers, boolean counters, boolean historyDigest) {
|
||||
ProbeOptions {
|
||||
if (threads < 1) {
|
||||
throw new IllegalArgumentException("Thread count must be at least 1.");
|
||||
@@ -196,7 +196,8 @@ public final class GenerationProbe {
|
||||
booleanProperty("iris.probe.hydrologyPreplan"),
|
||||
booleanProperty("iris.probe.historyRouter"),
|
||||
booleanProperty("iris.probe.productionBuffers"),
|
||||
booleanProperty("iris.generation.counters"));
|
||||
booleanProperty("iris.generation.counters"),
|
||||
booleanProperty("iris.probe.historyDigest"));
|
||||
}
|
||||
|
||||
private static boolean booleanProperty(String key) {
|
||||
@@ -437,7 +438,8 @@ public final class GenerationProbe {
|
||||
+ " timeMs=" + String.format(Locale.ROOT, "%.3f", nanosToMillis(engineReadyNanos)));
|
||||
System.out.println("IRIS_GENPROBE_MANTLE version=1 radius=" + engine.getMantle().getRadius()
|
||||
+ " real_radius=" + engine.getMantle().getRealRadius());
|
||||
ProbeChunkGenerator generator = new ProbeChunkGenerator(engine, router, options.productionBuffers());
|
||||
ProbeChunkGenerator generator = new ProbeChunkGenerator(engine, router, options.productionBuffers(),
|
||||
options.historyDigest());
|
||||
long nativeSamplesBefore = FractalBillowSimplexNoise.nativeSampleCount();
|
||||
GenerationResult generation = generateWithProfile(generator, configuration, options);
|
||||
verifyBackend(nativeSamplesBefore);
|
||||
@@ -445,6 +447,9 @@ public final class GenerationProbe {
|
||||
System.out.println("IRIS_GENPROBE_BUFFERS version=1 mode=production biome_expanded_chunks="
|
||||
+ generator.expandedBiomeHunks());
|
||||
}
|
||||
if (generator.historySummary() != null) {
|
||||
System.out.println(generator.historySummary());
|
||||
}
|
||||
String status = generation.failedChunks() == 0 ? "PASS" : "FAIL";
|
||||
printGenerationFailures(generation);
|
||||
return new ProbeResult(
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Servers
|
||||
* Copyright (c) 2026 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package art.arcane.iris.probe;
|
||||
|
||||
import art.arcane.iris.world.history.ChunkGenerationSemantics;
|
||||
import art.arcane.iris.world.history.GenerationHistoryRuntimeRouter;
|
||||
import art.arcane.iris.world.history.SavedBiomeChunk;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.HexFormat;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentSkipListMap;
|
||||
|
||||
final class HistoryDigest {
|
||||
private final GenerationHistoryRuntimeRouter router;
|
||||
private final Map<Long, byte[]> chunks = new ConcurrentSkipListMap<>();
|
||||
|
||||
HistoryDigest(GenerationHistoryRuntimeRouter router) {
|
||||
this.router = router;
|
||||
}
|
||||
|
||||
void record(int chunkX, int chunkZ) throws IOException {
|
||||
StringBuilder canonical = new StringBuilder(4096);
|
||||
Optional<ChunkGenerationSemantics> semantics = router.history().semantics(chunkX, chunkZ);
|
||||
canonical.append(semantics.map(value -> value + "|" + value.pointsOfInterest()).orElse("none")).append('\n');
|
||||
Optional<SavedBiomeChunk> biomes = router.biomes().snapshot(chunkX, chunkZ);
|
||||
if (biomes.isEmpty()) {
|
||||
canonical.append("none");
|
||||
} else {
|
||||
SavedBiomeChunk chunk = biomes.get();
|
||||
canonical.append(chunk.header());
|
||||
for (int localZ = 0; localZ < 16; localZ++) {
|
||||
for (int localX = 0; localX < 16; localX++) {
|
||||
canonical.append('\n').append(chunk.column(localX, localZ));
|
||||
}
|
||||
}
|
||||
}
|
||||
chunks.put(((long) chunkX << 32) | (chunkZ & 0xFFFFFFFFL), sha256(canonical.toString().getBytes(StandardCharsets.UTF_8)));
|
||||
}
|
||||
|
||||
String summary() {
|
||||
MessageDigest digest = newDigest();
|
||||
ByteBuffer key = ByteBuffer.allocate(Long.BYTES);
|
||||
for (Map.Entry<Long, byte[]> entry : chunks.entrySet()) {
|
||||
key.clear();
|
||||
key.putLong(entry.getKey());
|
||||
digest.update(key.array());
|
||||
digest.update(entry.getValue());
|
||||
}
|
||||
return "IRIS_GENPROBE_HISTORY version=1 chunks=" + chunks.size()
|
||||
+ " digest=" + HexFormat.of().formatHex(digest.digest(), 0, 8);
|
||||
}
|
||||
|
||||
private static byte[] sha256(byte[] bytes) {
|
||||
return newDigest().digest(bytes);
|
||||
}
|
||||
|
||||
private static MessageDigest newDigest() {
|
||||
try {
|
||||
return MessageDigest.getInstance("SHA-256");
|
||||
} catch (NoSuchAlgorithmException missing) {
|
||||
throw new IllegalStateException("SHA-256 is unavailable", missing);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,12 +36,19 @@ final class ProbeChunkGenerator {
|
||||
private final Engine engine;
|
||||
private final GenerationHistoryRuntimeRouter router;
|
||||
private final boolean productionBuffers;
|
||||
private final HistoryDigest historyDigest;
|
||||
private final AtomicInteger expandedBiomeHunks = new AtomicInteger();
|
||||
|
||||
ProbeChunkGenerator(Engine engine, GenerationHistoryRuntimeRouter router, boolean productionBuffers) {
|
||||
ProbeChunkGenerator(Engine engine, GenerationHistoryRuntimeRouter router, boolean productionBuffers,
|
||||
boolean historyDigest) {
|
||||
this.engine = engine;
|
||||
this.router = router;
|
||||
this.productionBuffers = productionBuffers;
|
||||
this.historyDigest = historyDigest && router != null ? new HistoryDigest(router) : null;
|
||||
}
|
||||
|
||||
String historySummary() {
|
||||
return historyDigest == null ? null : historyDigest.summary();
|
||||
}
|
||||
|
||||
Engine engine() {
|
||||
@@ -64,6 +71,13 @@ final class ProbeChunkGenerator {
|
||||
failures.add(e);
|
||||
}
|
||||
long elapsed = System.nanoTime() - started;
|
||||
if (historyDigest != null && failures.isEmpty()) {
|
||||
try {
|
||||
historyDigest.record(coordinate.x(), coordinate.z());
|
||||
} catch (Throwable e) {
|
||||
failures.add(e);
|
||||
}
|
||||
}
|
||||
if (biomes instanceof ProductionBiomeHunk production && production.expanded()) {
|
||||
expandedBiomeHunks.incrementAndGet();
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ public final class GenerationProbeTest {
|
||||
assertEquals(1, defaults.threads());
|
||||
assertNull(defaults.dumpFile());
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> new GenerationProbe.ProbeOptions(0, null, false, false, false, false, false));
|
||||
() -> new GenerationProbe.ProbeOptions(0, null, false, false, false, false, false, false));
|
||||
assertFalse(defaults.hydrologyPreplan());
|
||||
assertFalse(defaults.historyRouter());
|
||||
assertFalse(defaults.productionBuffers());
|
||||
|
||||
Reference in new issue
Block a user