fix(perf): normalize entity hotspot lifetimes

This commit is contained in:
MoePus committed 2026-08-29 16:01:31 +09:00
1 parent fcd53a9668
commit d621322018
21 files changed
+190 -42

No files matched your search

@@ -51,8 +51,15 @@ public final class ClientPerfWorldState {
long tickCount,
long nanosPerSample) {
var cost = new PerfWorldHotspotIndex.Cost(entry.samples(), entry.maxSamplesQ8());
PerfReportData.Metric metric = cost.metric(tickCount, nanosPerSample);
return new Hotspot(entry.key(), entry.detailKind(), entry.registryId(), metric);
long measurementTicks = measurementTicks(tickCount, entry.measurementStartTick());
PerfReportData.Metric metric = cost.metric(measurementTicks, nanosPerSample);
return new Hotspot(entry.key(), entry.detailKind(), entry.registryId(),
measurementTicks, metric);
}
static long measurementTicks(long tickCount, long measurementStartTick) {
return tickCount <= 0L ? 0L
: Math.max(1L, tickCount - measurementStartTick + 1L);
}
public static Snapshot snapshot() {
@@ -67,6 +74,7 @@ public final class ClientPerfWorldState {
public record Hotspot(PerfWorldHotspotIndex.Key key,
TickDetailKind detailKind,
int registryId,
long measurementTicks,
PerfReportData.Metric metric) {
}
@@ -44,7 +44,8 @@ final class WorldEntityFlameClusters {
mspt, Math.max(mspt, maximum), samples);
var anchor = new Anchor(centerX(input.bounds()), input.bounds().maxY,
centerZ(input.bounds()));
var visual = new Visual(ObjectPerformanceScale.effectiveSamples(metric, tickCount),
var visual = new Visual(ObjectPerformanceScale.effectiveSamples(
metric, measurementTicks(input, tickCount)),
updatePhase(0x811C9DC5, input.stableId()) & 0x7FFF, 1);
return new Marker(anchor, metric, visual);
}
@@ -54,6 +55,8 @@ final class WorldEntityFlameClusters {
double totalMspt = 0.0D;
double totalMax = 0.0D;
long totalSamples = 0L;
double averageSamples = 0.0D;
double maximumSamples = 0.0D;
double totalWeight = 0.0D;
double x = 0.0D;
double z = 0.0D;
@@ -66,6 +69,9 @@ final class WorldEntityFlameClusters {
totalMspt += mspt;
totalMax += maximum;
totalSamples = saturatedAdd(totalSamples, Math.max(0L, input.metric().samples()));
long inputTicks = measurementTicks(input, tickCount);
averageSamples += ObjectPerformanceScale.averageSamples(input.metric(), inputTicks);
maximumSamples += ObjectPerformanceScale.estimatedMaxSamples(input.metric(), inputTicks);
totalWeight += weight;
x += centerX(input.bounds()) * weight;
z += centerZ(input.bounds()) * weight;
@@ -75,10 +81,14 @@ final class WorldEntityFlameClusters {
PerfReportData.Metric metric = new PerfReportData.Metric(
totalMspt, Math.max(totalMspt, totalMax), totalSamples);
return new Marker(new Anchor(x / totalWeight, y, z / totalWeight), metric,
new Visual(ObjectPerformanceScale.effectiveSamples(metric, tickCount),
new Visual(ObjectPerformanceScale.effectiveSamples(averageSamples, maximumSamples),
phase & 0x7FFF, group.size()));
}
private static long measurementTicks(Input input, long fallback) {
return input.measurementTicks() > 0L ? input.measurementTicks() : fallback;
}
private static double positiveFinite(double value) {
return Double.isFinite(value) && value > 0.0D ? value : 0.0D;
}
@@ -103,7 +113,11 @@ final class WorldEntityFlameClusters {
return (int) Math.floor(coordinate / CELL_SIZE);
}
record Input(AABB bounds, PerfReportData.Metric metric, int stableId) {
record Input(AABB bounds, PerfReportData.Metric metric,
long measurementTicks, int stableId) {
Input(AABB bounds, PerfReportData.Metric metric, int stableId) {
this(bounds, metric, 0L, stableId);
}
}
record Anchor(double x, double y, double z) {
@@ -78,7 +78,7 @@ final class WorldPerfObjectRenderer {
}
for (ClientPerfWorldState.Hotspot hotspot : snapshot.hotspots()) {
double effective = ObjectPerformanceScale.effectiveSamples(
hotspot.metric(), snapshot.tickCount());
hotspot.metric(), hotspot.measurementTicks());
if (WorldOverlayMath.shouldRender(effective, currentFilter)) {
next.add(new Candidate(hotspot, hotspot.metric(), effective));
if (next.size() == WorldPerfObjectBuffers.MAX_MARKER_COUNT) {
@@ -109,7 +109,8 @@ final class WorldPerfObjectRenderer {
}
if (candidate.hotspot().key().kind() == PerfWorldHotspotIndex.Kind.ENTITY) {
var input = new WorldEntityFlameClusters.Input(
bounds, candidate.metric(), candidate.hotspot().key().entityId());
bounds, candidate.metric(), candidate.hotspot().measurementTicks(),
candidate.hotspot().key().entityId());
if (shouldClusterEntity(candidate.hotspot().detailKind())) {
itemEntities.add(input);
} else {
@@ -26,7 +26,13 @@ public final class ObjectPerformanceScale {
public static double effectiveSamples(PerfReportData.Metric metric, long tickCount) {
double average = averageSamples(metric, tickCount);
double burst = Math.max(0.0D, estimatedMaxSamples(metric, tickCount) - average);
return effectiveSamples(average, estimatedMaxSamples(metric, tickCount));
}
public static double effectiveSamples(double averageSamples, double estimatedMaxSamples) {
double average = bounded(averageSamples);
double maximum = bounded(estimatedMaxSamples);
double burst = Math.max(0.0D, maximum - average);
return Math.min(MAX_SAMPLE_VALUE, average + Math.log1p(burst) / LOG_TWO);
}
@@ -36,7 +42,11 @@ public final class ObjectPerformanceScale {
}
double averageSamples = samples / (double) tickCount;
double estimatedMaxSamples = maxSamplesQ8 / (double) PerfWorldHotspotIndex.MAX_SAMPLES_SCALE;
double excess = Math.max(0.0D, estimatedMaxSamples - averageSamples);
return averageSamples + Math.log1p(excess) / Math.log(2.0D);
return effectiveSamples(averageSamples, estimatedMaxSamples);
}
private static double bounded(double value) {
return Double.isFinite(value) && value > 0.0D
? Math.min(MAX_SAMPLE_VALUE, value) : 0.0D;
}
}
@@ -20,6 +20,9 @@ public record PerfWorldHotspotIndex(long streamEpoch,
throw new IllegalArgumentException("Hotspot index scalars must be non-negative");
}
entries = List.copyOf(entries);
if (entries.stream().anyMatch(entry -> entry.measurementStartTick() > tickCount)) {
throw new IllegalArgumentException("Hotspot measurement starts after the snapshot");
}
}
public static PerfWorldHotspotIndex empty(long streamEpoch, long revision, long tickCount) {
@@ -52,6 +55,7 @@ public record PerfWorldHotspotIndex(long streamEpoch,
int z,
TickDetailKind detailKind,
ResourceLocation detailId,
long measurementStartTick,
long samples,
int maxSamplesQ8) {
public Entry {
@@ -59,8 +63,8 @@ public record PerfWorldHotspotIndex(long streamEpoch,
Objects.requireNonNull(dimension, "dimension");
Objects.requireNonNull(detailKind, "detailKind");
Objects.requireNonNull(detailId, "detailId");
if (samples < 0L || maxSamplesQ8 < 0) {
throw new IllegalArgumentException("Hotspot samples must be non-negative");
if (measurementStartTick <= 0L || samples < 0L || maxSamplesQ8 < 0) {
throw new IllegalArgumentException("Invalid hotspot measurement data");
}
key.validateDetailKind(detailKind);
if (key.kind() == Kind.BLOCK_ENTITY
@@ -69,6 +73,11 @@ public record PerfWorldHotspotIndex(long streamEpoch,
}
}
public long measurementTicks(long totalTicks) {
return totalTicks <= 0L ? 0L
: Math.max(1L, totalTicks - measurementStartTick + 1L);
}
}
public record Key(Kind kind, int entityId, long blockPosition) {
@@ -177,7 +177,8 @@ final class PerfObjectSnapshotBuilder {
PerfReportData.Metric metric,
long tickCount) {
return new ObjectCandidate(key, aggregate, metric,
ObjectPerformanceScale.effectiveSamples(metric, tickCount),
ObjectPerformanceScale.effectiveSamples(
metric, aggregate.measurementTicks(tickCount)),
StableObjectId.from(key, aggregate), tickCount);
}
@@ -203,7 +204,7 @@ final class PerfObjectSnapshotBuilder {
PerfWorldHotspotIndex.Cost cost = aggregate.stats.hotspotCost(tickCount);
return new PerfWorldHotspotIndex.Entry(hotspotKey(), key.dimension(),
aggregate.x, aggregate.y, aggregate.z, key.detailKind(), key.detailId(),
cost.samples(), cost.maxSamplesQ8());
aggregate.measurementStartTick, cost.samples(), cost.maxSamplesQ8());
}
static ObjectCandidate newer(ObjectCandidate left, ObjectCandidate right) {
@@ -251,6 +251,7 @@ final class PerfSampleAggregator {
boolean chunkPosition;
int x, y, z;
long lastSeenTick;
long measurementStartTick = 1L;
void record(TickSamples.TickCount count, long tick) {
positioned = count.positioned;
@@ -259,6 +260,10 @@ final class PerfSampleAggregator {
y = count.y;
z = count.z;
lastSeenTick = tick;
if (count.entityAgeTicks >= 0) {
long age = Math.max(1L, count.entityAgeTicks);
measurementStartTick = Math.max(1L, tick - age + 1L);
}
stats.record(count.count, tick);
}
@@ -283,7 +288,11 @@ final class PerfSampleAggregator {
}
PerfReportData.Metric metric(long ticks, double nanosPerSample) {
return stats.metric(ticks, nanosPerSample);
return stats.metric(ticks, measurementTicks(ticks), nanosPerSample);
}
long measurementTicks(long ticks) {
return ticks <= 0L ? 0L : Math.max(1L, ticks - measurementStartTick + 1L);
}
TickSamples.ChunkKey chunkKey(TickSamples.TickableKey key) {
@@ -174,7 +174,25 @@ public final class PerfWorkTracker {
int z,
boolean chunkPosition,
String eventLoop,
String displayName) {
String displayName,
int entityAgeTicks) {
public Snapshot(TickType type,
ResourceLocation dimension,
Class<?> workClass,
ResourceLocation detailId,
TickDetailKind detailKind,
int entityId,
boolean positioned,
int x,
int y,
int z,
boolean chunkPosition,
String eventLoop,
String displayName) {
this(type, dimension, workClass, detailId, detailKind, entityId,
positioned, x, y, z, chunkPosition, eventLoop, displayName, -1);
}
public Snapshot(TickType type,
ResourceLocation dimension,
Class<?> workClass,
@@ -188,7 +206,7 @@ public final class PerfWorkTracker {
boolean chunkPosition,
String eventLoop) {
this(type, dimension, workClass, detailId, detailKind, entityId,
positioned, x, y, z, chunkPosition, eventLoop, "");
positioned, x, y, z, chunkPosition, eventLoop, "", -1);
}
}
@@ -208,6 +226,7 @@ public final class PerfWorkTracker {
private boolean chunkPosition;
private String eventLoop;
private String displayName;
private int entityAgeTicks;
void publishChunk(ResourceLocation dimension, LevelChunk chunk) {
var pos = chunk.getPos();
@@ -222,6 +241,7 @@ public final class PerfWorkTracker {
z = pos.z;
chunkPosition = true;
eventLoop = null;
entityAgeTicks = -1;
finishPublish();
}
@@ -242,6 +262,7 @@ public final class PerfWorkTracker {
z = entity.getBlockZ();
chunkPosition = false;
eventLoop = null;
entityAgeTicks = Math.max(1, entity.tickCount + 1);
finishPublish();
}
@@ -258,6 +279,7 @@ public final class PerfWorkTracker {
z = pos.getZ();
chunkPosition = false;
eventLoop = null;
entityAgeTicks = -1;
finishPublish();
}
@@ -273,6 +295,7 @@ public final class PerfWorkTracker {
z = 0;
chunkPosition = false;
this.eventLoop = eventLoop;
entityAgeTicks = -1;
finishPublish();
}
@@ -301,7 +324,7 @@ public final class PerfWorkTracker {
}
Snapshot result = new Snapshot(type, dimension, workClass, detailId, detailKind,
entityId, positioned,
x, y, z, chunkPosition, eventLoop, displayName);
x, y, z, chunkPosition, eventLoop, displayName, entityAgeTicks);
return before == version ? result : null;
}
}
@@ -35,9 +35,14 @@ final class SampleStatistics {
}
PerfReportData.Metric metric(long totalTicks, double nanosPerSample) {
advanceWindows(totalTicks);
double mspt = totalTicks == 0 ? 0.0D
: totalSamples * nanosPerSample / NANOS_PER_MILLISECOND / totalTicks;
return metric(totalTicks, totalTicks, nanosPerSample);
}
PerfReportData.Metric metric(long currentTick, long measurementTicks,
double nanosPerSample) {
advanceWindows(currentTick);
double mspt = measurementTicks == 0L ? 0.0D
: totalSamples * nanosPerSample / NANOS_PER_MILLISECOND / measurementTicks;
double estimatedMaxSamples = estimatedMaxSamples();
double estimatedMax = estimatedMaxSamples * nanosPerSample / NANOS_PER_MILLISECOND;
return new PerfReportData.Metric(mspt, Math.max(mspt, estimatedMax), Math.round(totalSamples));
@@ -104,6 +104,7 @@ final class TickSamples {
int x;
int y;
int z;
int entityAgeTicks = -1;
void record(PerfWorkTracker.Snapshot snapshot) {
count++;
@@ -112,6 +113,7 @@ final class TickSamples {
x = snapshot.x();
y = snapshot.y();
z = snapshot.z();
entityAgeTicks = snapshot.entityAgeTicks();
}
void merge(TickCount source) {
@@ -121,6 +123,7 @@ final class TickSamples {
x = source.x;
y = source.y;
z = source.z;
entityAgeTicks = Math.max(entityAgeTicks, source.entityAgeTicks);
}
}
}
@@ -45,6 +45,9 @@ public record PerfWorldHotspotPayload(long streamEpoch,
removals = List.copyOf(removals);
validateSize(upserts.size());
validateSize(removals.size());
if (upserts.stream().anyMatch(entry -> entry.measurementStartTick() > tickCount)) {
throw new IllegalArgumentException("Hotspot measurement starts after the payload");
}
}
@Override
@@ -84,6 +87,7 @@ public record PerfWorldHotspotPayload(long streamEpoch,
writeKey(buffer, entry.key);
buffer.writeByte(entry.detailKind.ordinal());
buffer.writeVarInt(entry.registryId);
buffer.writeVarLong(entry.measurementStartTick);
buffer.writeVarLong(entry.samples);
buffer.writeVarInt(entry.maxSamplesQ8);
}
@@ -96,7 +100,7 @@ public record PerfWorldHotspotPayload(long streamEpoch,
throw new IllegalArgumentException("Unknown hotspot detail kind: " + detailOrdinal);
}
return new Entry(key, kinds[detailOrdinal], buffer.readVarInt(),
buffer.readVarLong(), buffer.readVarInt());
buffer.readVarLong(), buffer.readVarLong(), buffer.readVarInt());
}
private static void writeKey(
@@ -140,12 +144,14 @@ public record PerfWorldHotspotPayload(long streamEpoch,
public record Entry(PerfWorldHotspotIndex.Key key,
TickDetailKind detailKind,
int registryId,
long measurementStartTick,
long samples,
int maxSamplesQ8) {
public Entry {
Objects.requireNonNull(key, "key");
Objects.requireNonNull(detailKind, "detailKind");
if (registryId < 0 || samples < 0L || maxSamplesQ8 < 0) {
if (registryId < 0 || measurementStartTick <= 0L
|| samples < 0L || maxSamplesQ8 < 0) {
throw new IllegalArgumentException("Invalid compact hotspot entry");
}
key.validateDetailKind(detailKind);
@@ -45,7 +45,8 @@ final class PerfWorldHotspotResolver {
String dimension = entity.level().dimension().location().toString();
return new PerfWorldHotspotIndex.Entry(entry.key(), dimension,
entity.getBlockX(), entity.getBlockY(), entity.getBlockZ(),
entry.detailKind(), entry.detailId(), entry.samples(), entry.maxSamplesQ8());
entry.detailKind(), entry.detailId(), entry.measurementStartTick(),
entry.samples(), entry.maxSamplesQ8());
}
private static Entity findEntity(PerfWorldHotspotIndex.Entry entry,
@@ -120,7 +120,7 @@ final class PerfWorldHotspotStream {
int registryId = registryIds.resolve(entry.detailKind(), entry.detailId());
return registryId < 0 ? null : new PerfWorldHotspotPayload.Entry(
entry.key(), entry.detailKind(), registryId,
entry.samples(), entry.maxSamplesQ8());
entry.measurementStartTick(), entry.samples(), entry.maxSamplesQ8());
}
private static int builtInRegistryId(TickDetailKind kind, ResourceLocation id) {
@@ -15,7 +15,7 @@ import net.neoforged.neoforge.network.registration.NetworkRegistry;
@EventBusSubscriber(modid = ServerWarashi.MODID, bus = EventBusSubscriber.Bus.MOD)
public final class WarashiNetwork {
static final String NETWORK_VERSION = "6";
static final String NETWORK_VERSION = "7";
private WarashiNetwork() {
}
@@ -45,7 +45,7 @@ class ClientPerfStateCoordinatorTest {
void clearAllResetsReportMapAndWorldStores() {
ClientPerfStateCoordinator.applyFullReport(report(), true);
var entry = new PerfWorldHotspotPayload.Entry(
PerfWorldHotspotIndex.Key.entity(7), TickDetailKind.ENTITY, 1, 2L, 256);
PerfWorldHotspotIndex.Key.entity(7), TickDetailKind.ENTITY, 1, 1L, 2L, 256);
ClientPerfStateCoordinator.applyWorldDelta(new PerfWorldHotspotPayload(
1L, 0L, 1L, 20L, 1_000_000L,
ResourceLocation.parse("minecraft:overworld"), true,
@@ -26,7 +26,7 @@ class ClientPerfWorldStateTest {
@Test
void scalarOnlyDeltaRefreshesMetricWithoutRepeatingEntry() {
var entry = new PerfWorldHotspotPayload.Entry(
KEY, TickDetailKind.ENTITY, 4, 20L, 512);
KEY, TickDetailKind.ENTITY, 4, 1L, 20L, 512);
assertTrue(ClientPerfWorldState.apply(payload(0L, 20L, 20L, 1_000_000L,
true, List.of(entry), List.of())));
assertEquals(1.0D, metric().mspt(), 1.0E-9D);
@@ -40,7 +40,7 @@ class ClientPerfWorldStateTest {
@Test
void rejectsDeltaWithWrongBaseAndAppliesRemoval() {
var entry = new PerfWorldHotspotPayload.Entry(
KEY, TickDetailKind.ENTITY, 4, 2L, 256);
KEY, TickDetailKind.ENTITY, 4, 1L, 2L, 256);
ClientPerfWorldState.apply(payload(
0L, 20L, 20L, 1_000_000L, true, List.of(entry), List.of()));
assertFalse(ClientPerfWorldState.apply(payload(
@@ -53,7 +53,7 @@ class ClientPerfWorldStateTest {
@Test
void rejectsOlderFullSnapshotButAcceptsANewStreamEpoch() {
var entry = new PerfWorldHotspotPayload.Entry(
KEY, TickDetailKind.ENTITY, 4, 2L, 256);
KEY, TickDetailKind.ENTITY, 4, 1L, 2L, 256);
assertTrue(ClientPerfWorldState.apply(new PerfWorldHotspotPayload(
7L, 0L, 20L, 20L, 1_000_000L, DIMENSION,
true, List.of(entry), List.of())));
@@ -70,6 +70,18 @@ class ClientPerfWorldStateTest {
assertEquals(1L, ClientPerfWorldState.snapshot().protocolRevision());
}
@Test
void dividesEntityCostByTicksSinceItAppeared() {
var entry = new PerfWorldHotspotPayload.Entry(
KEY, TickDetailKind.ENTITY, 4, 91L, 20L, 512);
assertTrue(ClientPerfWorldState.apply(payload(
0L, 100L, 100L, 1_000_000L, true, List.of(entry), List.of())));
assertEquals(2.0D, metric().mspt(), 1.0E-9D);
assertEquals(10L, ClientPerfWorldState.snapshot().hotspots().get(0).measurementTicks());
}
private static com.moepus.serverwarashi.common.modules.performance.report.PerfReportData.Metric metric() {
return ClientPerfWorldState.snapshot().hotspots().get(0).metric();
}
@@ -69,6 +69,20 @@ class WorldEntityFlameClustersTest {
assertEquals(clustered.z(), single.z(), 0.000_001D);
}
@Test
void combinesRatesFromMembersWithDifferentMeasurementWindows() {
var recent = new WorldEntityFlameClusters.Input(
bounds(new Point(0.0D, 0.0D)), metric(1.0D, 1.0D, 10L), 10L, 1);
var old = new WorldEntityFlameClusters.Input(
bounds(new Point(0.2D, 0.0D)), metric(1.0D, 1.0D, 100L), 100L, 2);
WorldEntityFlameClusters.Marker marker =
WorldEntityFlameClusters.cluster(List.of(recent, old), 100L).get(0);
assertEquals(2.0D, marker.metric().mspt(), 0.000_001D);
assertEquals(2.0D, marker.effectiveSamples(), 0.000_001D);
}
@Test
void clustersTheMaximumCoincidentInputBudgetWithoutPairMaterialization() {
List<WorldEntityFlameClusters.Input> inputs = new ArrayList<>();
@@ -85,10 +99,12 @@ class WorldEntityFlameClustersTest {
private static WorldEntityFlameClusters.Input input(
int id, Point point, PerfReportData.Metric metric) {
return new WorldEntityFlameClusters.Input(
new AABB(point.x() - 0.3D, 64.0D, point.z() - 0.3D,
point.x() + 0.3D, 65.8D, point.z() + 0.3D),
metric, id);
return new WorldEntityFlameClusters.Input(bounds(point), metric, id);
}
private static AABB bounds(Point point) {
return new AABB(point.x() - 0.3D, 64.0D, point.z() - 0.3D,
point.x() + 0.3D, 65.8D, point.z() + 0.3D);
}
private static WorldEntityFlameClusters.Input input(int id, AABB bounds) {
@@ -124,6 +124,35 @@ class PerfSampleAggregatorTest {
assertTrue(report.chunks().isEmpty());
}
@Test
void limitsEntityAverageToItsLifetimeInsideTheSamplingSession() {
PerfSampleAggregator aggregator = new PerfSampleAggregator(23L);
for (int tick = 1; tick <= 90; tick++) {
aggregator.accept(new TickSamples(null));
}
TickSamples spawned = new TickSamples(null);
spawned.sample(new PerfWorkTracker.Snapshot(TickType.ENTITY,
ResourceLocation.parse("test:level"), Cow.class,
ResourceLocation.parse("minecraft:cow"), TickDetailKind.ENTITY, 7,
true, 8, 64, 8, false, null, "", 1));
aggregator.accept(spawned);
for (int tick = 92; tick <= 100; tick++) {
aggregator.accept(new TickSamples(null));
}
PerfSampleAggregator.BuildSnapshot build =
aggregator.buildSnapshot(milliseconds(100L), 1L, 1_000);
double sampleMillis = observedMillis(100L, 1);
PerfReportData.TickableEntry entity = build.report().tickables().get(0);
PerfWorldHotspotIndex.Entry hotspot = build.hotspots().entries().get(0);
assertEquals(sampleMillis / 10.0D, entity.metric().mspt(), 0.000_001D);
assertEquals(sampleMillis / 100.0D,
build.report().chunks().get(0).metric().mspt(), 0.000_001D);
assertEquals(91L, hotspot.measurementStartTick());
assertEquals(10L, hotspot.measurementTicks(build.hotspots().tickCount()));
}
@Test
void capturesRareMultiSampleEntityTickAsBurstMax() {
PerfSampleAggregator aggregator = new PerfSampleAggregator(8L);
@@ -18,10 +18,11 @@ class PerfWorldHotspotPayloadTest {
@Test
void roundTripsCompactEntriesWithoutUuidIdentity() {
var entity = new PerfWorldHotspotPayload.Entry(
PerfWorldHotspotIndex.Key.entity(42), TickDetailKind.ENTITY, 7, 123L, 640);
PerfWorldHotspotIndex.Key.entity(42), TickDetailKind.ENTITY,
7, 17L, 123L, 640);
var blockEntity = new PerfWorldHotspotPayload.Entry(
PerfWorldHotspotIndex.Key.blockEntity(3, 64, -2),
TickDetailKind.BLOCK, 11, 9L, 384);
TickDetailKind.BLOCK, 11, 1L, 9L, 384);
var expected = new PerfWorldHotspotPayload(5L, 0L, 20L, 20L, 1_250_000L,
ResourceLocation.parse("minecraft:overworld"), true,
List.of(entity, blockEntity), List.of());
@@ -65,16 +66,16 @@ class PerfWorldHotspotPayloadTest {
assertThrows(IllegalArgumentException.class, () ->
new PerfWorldHotspotIndex.Entry(blockKey, "minecraft:overworld",
4, 64, -2, TickDetailKind.BLOCK, stone, 1L, 256));
4, 64, -2, TickDetailKind.BLOCK, stone, 1L, 1L, 256));
assertThrows(IllegalArgumentException.class, () ->
new PerfWorldHotspotIndex.Entry(blockKey, "minecraft:overworld",
3, 64, -2, TickDetailKind.ENTITY, stone, 1L, 256));
3, 64, -2, TickDetailKind.ENTITY, stone, 1L, 1L, 256));
assertThrows(IllegalArgumentException.class, () ->
new PerfWorldHotspotPayload.Entry(
blockKey, TickDetailKind.ENTITY, 1, 1L, 256));
blockKey, TickDetailKind.ENTITY, 1, 1L, 1L, 256));
assertThrows(IllegalArgumentException.class, () ->
new PerfWorldHotspotPayload.Entry(
PerfWorldHotspotIndex.Key.entity(4),
TickDetailKind.BLOCK, 1, 1L, 256));
TickDetailKind.BLOCK, 1, 1L, 1L, 256));
}
}
@@ -122,6 +122,6 @@ class PerfWorldHotspotStreamTest {
int x, int y, int z, long samples, ResourceLocation detailId) {
return new PerfWorldHotspotIndex.Entry(
PerfWorldHotspotIndex.Key.blockEntity(x, y, z), DIMENSION.toString(), x, y, z,
TickDetailKind.BLOCK, detailId, samples, (int) samples * 256);
TickDetailKind.BLOCK, detailId, 1L, samples, (int) samples * 256);
}
}
@@ -77,7 +77,7 @@ class WeakChunkPayloadTest {
@Test
void networkProtocolIsVersionSix() {
assertEquals("6", WarashiNetwork.NETWORK_VERSION);
assertEquals("7", WarashiNetwork.NETWORK_VERSION);
}
private static RegistryFriendlyByteBuf buffer() {