(Paper PR) Workaround for Client Lag Spikes (MC-162253)
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
From e6b12fb1c5cff6e95e7186de62ac8e4580560387 Mon Sep 17 00:00:00 2001
|
||||
From: MeFisto94 <MeFisto94@users.noreply.github.com>
|
||||
Date: Tue, 12 May 2020 23:02:43 +0200
|
||||
Subject: [PATCH] (Paper PR) Workaround for Client Lag Spikes (MC-162253)
|
||||
|
||||
When crossing certain chunk boundaries, the client needlessly
|
||||
calculates light maps for chunk neighbours. In some specific map
|
||||
configurations, these calculations cause a 500ms+ freeze on the Client.
|
||||
|
||||
This patch basically serves as a workaround by sending light maps
|
||||
to the client, so that it doesn't attempt to calculate them.
|
||||
This mitigates the frametime impact to a minimum (but it's still there).
|
||||
|
||||
The best solution would be Mojang fixing this issue, and in an alternative
|
||||
way, clients using mods such as Starlight or Phosphor.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
index 961787473601fcddef98f321eb4fb5127c0f8ebc..e12ac8ab0bcd79fe2a9c34aa5e4edd649d9be1be 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
@@ -2127,27 +2127,44 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
|
||||
}
|
||||
|
||||
- // Paper start
|
||||
+ /*
|
||||
+ // Paper start
|
||||
private static int getLightMask(final LevelChunk chunk) {
|
||||
final net.minecraft.world.level.chunk.LevelChunkSection[] chunkSections = chunk.getSections();
|
||||
int mask = 0;
|
||||
|
||||
for (int i = 0; i < chunkSections.length; ++i) {
|
||||
- /*
|
||||
+
|
||||
|
||||
|
||||
Lightmasks have 18 bits, from the -1 (void) section until the 17th (air) section.
|
||||
Sections go from 0..16. Now whenever a section is not empty, it can potentially change lighting for the section itself, the section below and the section above, hence the bitmask 111b, which is 7d.
|
||||
|
||||
- */
|
||||
+
|
||||
mask |= (net.minecraft.world.level.chunk.LevelChunkSection.isEmpty(chunkSections[i]) ? 0 : 7) << i;
|
||||
- }
|
||||
+ */
|
||||
+ // Paper start - Fix MC-162253
|
||||
+ private static final BitSet lightMask(LevelChunk chunk) {
|
||||
+ net.minecraft.world.level.chunk.LevelChunkSection[] sections = chunk.getSections();
|
||||
+ BitSet mask = new BitSet(sections.length + 2); // One light section added above and below
|
||||
+ for (int i = 0; i < sections.length; i++) {
|
||||
+ if (!net.minecraft.world.level.chunk.LevelChunkSection.isEmpty(sections[i])) {
|
||||
+ // Whenever a section is not empty, it can potentially change lighting for the section itself, the section below and the section above
|
||||
+ // Add one to account for the light section below min y
|
||||
+ mask.set(i + 0);
|
||||
+ mask.set(i + 1);
|
||||
+ mask.set(i + 2);
|
||||
+ }
|
||||
+ }
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
- private static int getCeilingLightMask(final LevelChunk chunk) {
|
||||
+ /*
|
||||
+ private static int getCeilingLightMask(final LevelChunk chunk) {
|
||||
int mask = getLightMask(chunk);
|
||||
+ */
|
||||
+ private static final BitSet ceilingLightMask(LevelChunk chunk) {
|
||||
|
||||
/*
|
||||
It is similar to get highest bit, it would turn an 001010 into an 001111 so basically the highest bit and all below.
|
||||
@@ -2158,20 +2175,66 @@ Sections go from 0..16. Now whenever a section is not empty, it can potentially
|
||||
@TODO: Implement Leafs suggestion
|
||||
either use Integer#numberOfLeadingZeros or document what this bithack is supposed to be doing then
|
||||
*/
|
||||
- mask |= mask >> 1;
|
||||
+ /*
|
||||
+ mask |= mask >> 1;
|
||||
mask |= mask >> 2;
|
||||
mask |= mask >> 4;
|
||||
mask |= mask >> 8;
|
||||
mask |= mask >> 16;
|
||||
|
||||
return mask;
|
||||
+ */
|
||||
+ net.minecraft.world.level.chunk.LevelChunkSection[] sections = chunk.getSections();
|
||||
+ for (int i = sections.length - 1; i >= 0; i--) {
|
||||
+ if (!net.minecraft.world.level.chunk.LevelChunkSection.isEmpty(sections[i])) {
|
||||
+ int heighest = i + 3; // Add one to account for the light section below min y and one because blocks might change the light in the section above, and one because toIndex in BitSet is exclusive
|
||||
+ BitSet mask = new BitSet(heighest);
|
||||
+ mask.set(0, heighest);
|
||||
+ return mask;
|
||||
+ }
|
||||
+ }
|
||||
+ return new BitSet();
|
||||
}
|
||||
- // Paper end
|
||||
+ // Paper end - Fix MC-162253
|
||||
|
||||
public void playerLoadedChunk(ServerPlayer player, Packet<?>[] packets, LevelChunk chunk) {
|
||||
if (packets[0] == null) {
|
||||
packets[0] = new ClientboundLevelChunkPacket(chunk, chunk.level.chunkPacketBlockController.shouldModify(player, chunk)); // Paper - Ani-Xray - Bypass
|
||||
packets[1] = new ClientboundLightUpdatePacket(chunk.getPos(), this.lightEngine, (BitSet) null, (BitSet) null, true);
|
||||
+
|
||||
+ // Paper start - Fix MC-162253
|
||||
+ final int viewDistance = this.playerChunkManager.getData(player).getTargetSendViewDistance();
|
||||
+ final int playerChunkX = (int) (player.getX() / 16);
|
||||
+ final int playerChunkZ = (int) (player.getZ() / 16);
|
||||
+
|
||||
+ final BitSet lightMask = lightMask(chunk);
|
||||
+ for (int x = -1; x <= 1; x++) {
|
||||
+ for (int z = -1; z <= 1; z++) {
|
||||
+ if (x == 0 && z == 0) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ if (!chunk.isNeighbourLoaded(x, z)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ final int distX = Math.abs(playerChunkX - (chunk.getPos().x + x));
|
||||
+ final int distZ = Math.abs(playerChunkZ - (chunk.getPos().z + z));
|
||||
+ if (Math.max(distX, distZ) > viewDistance) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ final LevelChunk neighbor = chunk.getRelativeNeighbourIfLoaded(x, z);
|
||||
+ BitSet updateLightMask = (BitSet) lightMask.clone();
|
||||
+ updateLightMask.andNot(ceilingLightMask(neighbor));
|
||||
+ if (updateLightMask.isEmpty()) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ player.connection.send(new ClientboundLightUpdatePacket(new ChunkPos(chunk.getPos().x + x, chunk.getPos().z + z), lightEngine, updateLightMask, null, true));
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - Fix MC-162253
|
||||
}
|
||||
|
||||
player.trackChunk(chunk.getPos(), packets[0], packets[1]);
|
||||
Reference in New Issue
Block a user