Skip to content

Commit 189b0db

Browse files
committed
new: smoothChunkSendingRate
1 parent a7505f1 commit 189b0db

5 files changed

Lines changed: 123 additions & 1 deletion

File tree

c2me-notickvd/src/main/java/com/ishland/c2me/notickvd/MixinPlugin.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ public boolean shouldApplyMixin(String targetClassName, String mixinClassName) {
1313
return Config.enableExtRenderDistanceProtocol;
1414
}
1515

16+
if (mixinClassName.startsWith("com.ishland.c2me.notickvd.mixin.smooth_sending_rate.")) {
17+
return Config.smoothChunkSendingRate;
18+
}
19+
1620
return true;
1721
}
1822
}

c2me-notickvd/src/main/java/com/ishland/c2me/notickvd/common/Config.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,17 @@ Requires Fabric API (currently %s)
2323
""".formatted(C2MEConstants.EXT_RENDER_DISTANCE_ID, ModStatuses.fabric_networking_api_v1 ? "available" : "unavailable"))
2424
.getBoolean(true, false);
2525

26+
public static final boolean smoothChunkSendingRate = new ConfigSystem.ConfigAccessor()
27+
.key("noTickViewDistance.smoothChunkSendingRate")
28+
.comment("""
29+
Whether to attempt to smooth out chunk sending rate
30+
31+
Due to the nature of chunk loading and generation, chunks reach full status in bursts,
32+
which can cause frame time stability if the server also delivers chunks in a bursty way
33+
This config attempts to smooth out the bursty stream of chunks to help frame time stability
34+
""")
35+
.getBoolean(true, false);
36+
2637
public static final boolean ensureChunkCorrectness = new ConfigSystem.ConfigAccessor()
2738
.key("noTickViewDistance.ensureChunkCorrectness")
2839
.comment("Whether to ensure correct chunks within normal render distance \n" +
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.ishland.c2me.notickvd.common.smooth_sending_rate;
2+
3+
// Taken from the Linux kernel loadavg
4+
public class ExponentialMovingAverage {
5+
private final double expRise;
6+
private final double expFall;
7+
private double oldValue = 0;
8+
9+
public ExponentialMovingAverage(double tickSeconds, double tauRiseSeconds, double tauFallSeconds) {
10+
this.expRise = 1.0 / Math.exp(tickSeconds / tauRiseSeconds);
11+
this.expFall = 1.0 / Math.exp(tickSeconds / tauFallSeconds);
12+
}
13+
14+
public double onTick(double value) {
15+
double newValue =
16+
value > this.oldValue
17+
? this.oldValue * this.expRise + value * (1.0 - this.expRise)
18+
: this.oldValue * this.expFall + value * (1.0 - this.expFall);
19+
this.oldValue = newValue;
20+
return newValue;
21+
}
22+
23+
public double getCurrent() {
24+
return this.oldValue;
25+
}
26+
27+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package com.ishland.c2me.notickvd.mixin.smooth_sending_rate;
2+
3+
import com.ishland.c2me.notickvd.common.smooth_sending_rate.ExponentialMovingAverage;
4+
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
5+
import it.unimi.dsi.fastutil.longs.LongSet;
6+
import net.minecraft.server.network.ChunkDataSender;
7+
import net.minecraft.server.network.ServerPlayerEntity;
8+
import net.minecraft.text.Text;
9+
import net.minecraft.world.chunk.WorldChunk;
10+
import org.objectweb.asm.Opcodes;
11+
import org.spongepowered.asm.mixin.Final;
12+
import org.spongepowered.asm.mixin.Mixin;
13+
import org.spongepowered.asm.mixin.Shadow;
14+
import org.spongepowered.asm.mixin.Unique;
15+
import org.spongepowered.asm.mixin.injection.At;
16+
import org.spongepowered.asm.mixin.injection.Inject;
17+
import org.spongepowered.asm.mixin.injection.Redirect;
18+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
19+
20+
import java.util.List;
21+
22+
@Mixin(ChunkDataSender.class)
23+
public class MixinChunkDataSender {
24+
25+
@Shadow
26+
@Final
27+
private LongSet chunks;
28+
29+
@Shadow
30+
private float pending;
31+
@Shadow
32+
@Final
33+
private boolean local;
34+
@Shadow
35+
private float desiredBatchSize;
36+
@Unique
37+
private ExponentialMovingAverage c2me$ema;
38+
@Unique
39+
private ExponentialMovingAverage c2me$emaActual;
40+
@Unique
41+
private int c2me$lastTickSent = 0;
42+
43+
@Inject(method = "<init>", at = @At(value = "RETURN"))
44+
private void onInit(boolean local, CallbackInfo ci) {
45+
this.c2me$ema = new ExponentialMovingAverage(1.0 / 20.0, 10.0, 4.0);
46+
this.c2me$emaActual = new ExponentialMovingAverage(1.0 / 20.0, 0.1, 0.1);
47+
}
48+
49+
@Inject(method = "sendChunkBatches", at = @At(value = "HEAD"))
50+
private void onTick(ServerPlayerEntity player, CallbackInfo ci) {
51+
this.c2me$ema.onTick(this.chunks.size());
52+
this.c2me$emaActual.onTick(this.c2me$lastTickSent);
53+
// player.sendMessage(Text.of(String.format("Current target sending rate: %.1f cps, %.1f cps, actual: %.1f cps", this.c2me$getTargetSendingRate() * 20.0, this.desiredBatchSize * 20.0, this.c2me$emaActual.getCurrent() * 20.0)), true);
54+
this.c2me$lastTickSent = 0;
55+
}
56+
57+
@Redirect(method = "sendChunkBatches", at = @At(value = "FIELD", target = "Lnet/minecraft/server/network/ChunkDataSender;desiredBatchSize:F", opcode = Opcodes.GETFIELD), require = 2)
58+
private float redirectDesiredBatchSize(ChunkDataSender instance) {
59+
assert instance == (Object) this;
60+
return this.local ? (float) this.c2me$getTargetSendingRate() : Math.min(this.desiredBatchSize, (float) this.c2me$getTargetSendingRate());
61+
}
62+
63+
@Redirect(method = "makeBatch", at = @At(value = "FIELD", target = "Lnet/minecraft/server/network/ChunkDataSender;local:Z", opcode = Opcodes.GETFIELD))
64+
private boolean applyClampingToLocal(ChunkDataSender instance) {
65+
return false;
66+
}
67+
68+
@ModifyReturnValue(method = "makeBatch", at = @At("RETURN"))
69+
private List<WorldChunk> onBatch(List<WorldChunk> original) {
70+
this.c2me$lastTickSent = original.size();
71+
return original;
72+
}
73+
74+
@Unique
75+
private double c2me$getTargetSendingRate() {
76+
return (this.c2me$ema.getCurrent()) / 10.0F;
77+
}
78+
79+
}

c2me-notickvd/src/main/resources/c2me-notickvd.mixins.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
"MixinWorld",
1818
"MixinWorldChunk",
1919
"ext_render_distance.MixinServerConfigurationNetworkHandler",
20-
"ext_render_distance.MixinServerPlayNetworkHandler"
20+
"ext_render_distance.MixinServerPlayNetworkHandler",
21+
"smooth_sending_rate.MixinChunkDataSender"
2122
],
2223
"client": [
2324
"client.MixinIntegratedServer"

0 commit comments

Comments
 (0)