From cb0c301bafc4da3e6344720c735a5bd53355d177 Mon Sep 17 00:00:00 2001 From: RK_01 <50594595+RaphiMC@users.noreply.github.com> Date: Tue, 29 Oct 2024 12:51:47 +0100 Subject: [PATCH] Fix 1.21.1 -> 1.21.2 chunk loading edge case (#4212) Will usually happen on 1.8 servers or older --- .../v1_21to1_21_2/Protocol1_21To1_21_2.java | 2 + .../BlockItemPacketRewriter1_21_2.java | 51 ++++++++++++++++++- .../rewriter/EntityPacketRewriter1_21_2.java | 8 +++ .../storage/ChunkLoadTracker.java | 45 ++++++++++++++++ 4 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 common/src/main/java/com/viaversion/viaversion/protocols/v1_21to1_21_2/storage/ChunkLoadTracker.java diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_21to1_21_2/Protocol1_21To1_21_2.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_21to1_21_2/Protocol1_21To1_21_2.java index 768e29cb7..cf3f6a1cc 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_21to1_21_2/Protocol1_21To1_21_2.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_21to1_21_2/Protocol1_21To1_21_2.java @@ -48,6 +48,7 @@ import com.viaversion.viaversion.protocols.v1_21to1_21_2.rewriter.ComponentRewri import com.viaversion.viaversion.protocols.v1_21to1_21_2.rewriter.EntityPacketRewriter1_21_2; import com.viaversion.viaversion.protocols.v1_21to1_21_2.rewriter.ParticleRewriter1_21_2; import com.viaversion.viaversion.protocols.v1_21to1_21_2.storage.BundleStateTracker; +import com.viaversion.viaversion.protocols.v1_21to1_21_2.storage.ChunkLoadTracker; import com.viaversion.viaversion.protocols.v1_21to1_21_2.storage.PlayerPositionStorage; import com.viaversion.viaversion.rewriter.AttributeRewriter; import com.viaversion.viaversion.rewriter.SoundRewriter; @@ -231,6 +232,7 @@ public final class Protocol1_21To1_21_2 extends AbstractProtocol { + final Chunk chunk = blockRewriter.handleChunk1_19(wrapper, ChunkType1_20_2::new); + final Mappings blockEntityMappings = protocol.getMappingData().getBlockEntityMappings(); + if (blockEntityMappings != null) { + final List blockEntities = chunk.blockEntities(); + for (int i = 0; i < blockEntities.size(); i++) { + final BlockEntity blockEntity = blockEntities.get(i); + final int id = blockEntity.typeId(); + final int mappedId = blockEntityMappings.getNewIdOrDefault(id, id); + if (id != mappedId) { + blockEntities.set(i, blockEntity.withTypeId(mappedId)); + } + } + } + + final ChunkLoadTracker chunkLoadTracker = wrapper.user().get(ChunkLoadTracker.class); + if (chunkLoadTracker.isChunkLoaded(chunk.getX(), chunk.getZ())) { + // Unload the old chunk, so the new one can be loaded without graphical glitches + // Bundling it prevents the client from falling through the world during the chunk swap + final boolean isBundling = wrapper.user().get(BundleStateTracker.class).isBundling(); + if (!isBundling) { + final PacketWrapper bundleStart = wrapper.create(ClientboundPackets1_21_2.BUNDLE_DELIMITER); + bundleStart.send(Protocol1_21To1_21_2.class); + } + + final PacketWrapper forgetLevelChunk = wrapper.create(ClientboundPackets1_21_2.FORGET_LEVEL_CHUNK); + forgetLevelChunk.write(Types.CHUNK_POSITION, new ChunkPosition(chunk.getX(), chunk.getZ())); + forgetLevelChunk.send(Protocol1_21To1_21_2.class); + wrapper.send(Protocol1_21To1_21_2.class); + wrapper.cancel(); + + if (!isBundling) { + final PacketWrapper bundleEnd = wrapper.create(ClientboundPackets1_21_2.BUNDLE_DELIMITER); + bundleEnd.send(Protocol1_21To1_21_2.class); + } + } else { + chunkLoadTracker.addChunk(chunk.getX(), chunk.getZ()); + } + }); + protocol.registerClientbound(ClientboundPackets1_21.FORGET_LEVEL_CHUNK, wrapper -> { + final ChunkPosition chunkPosition = wrapper.passthrough(Types.CHUNK_POSITION); + wrapper.user().get(ChunkLoadTracker.class).removeChunk(chunkPosition.chunkX(), chunkPosition.chunkZ()); + }); } private void convertServerboundRecipeDisplayId(final PacketWrapper wrapper) { diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_21to1_21_2/rewriter/EntityPacketRewriter1_21_2.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_21to1_21_2/rewriter/EntityPacketRewriter1_21_2.java index ebb4e35eb..8c156304f 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_21to1_21_2/rewriter/EntityPacketRewriter1_21_2.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_21to1_21_2/rewriter/EntityPacketRewriter1_21_2.java @@ -18,6 +18,7 @@ package com.viaversion.viaversion.protocols.v1_21to1_21_2.rewriter; import com.viaversion.nbt.tag.CompoundTag; +import com.viaversion.viaversion.api.data.entity.EntityTracker; import com.viaversion.viaversion.api.minecraft.RegistryEntry; import com.viaversion.viaversion.api.minecraft.entities.EntityType; import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_21_2; @@ -33,6 +34,7 @@ import com.viaversion.viaversion.protocols.v1_21to1_21_2.Protocol1_21To1_21_2; import com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ClientboundPackets1_21_2; import com.viaversion.viaversion.protocols.v1_21to1_21_2.packet.ServerboundPackets1_21_2; import com.viaversion.viaversion.protocols.v1_21to1_21_2.storage.BundleStateTracker; +import com.viaversion.viaversion.protocols.v1_21to1_21_2.storage.ChunkLoadTracker; import com.viaversion.viaversion.protocols.v1_21to1_21_2.storage.ClientVehicleStorage; import com.viaversion.viaversion.protocols.v1_21to1_21_2.storage.PlayerPositionStorage; import com.viaversion.viaversion.rewriter.EntityRewriter; @@ -121,6 +123,12 @@ public final class EntityPacketRewriter1_21_2 extends EntityRewriter. + */ +package com.viaversion.viaversion.protocols.v1_21to1_21_2.storage; + +import com.viaversion.viaversion.api.connection.StorableObject; +import com.viaversion.viaversion.api.minecraft.ChunkPosition; +import java.util.HashSet; +import java.util.Set; + +public class ChunkLoadTracker implements StorableObject { + + private final Set loadedChunks = new HashSet<>(); + + public void addChunk(final int x, final int z) { + this.loadedChunks.add(ChunkPosition.chunkKey(x, z)); + } + + public void removeChunk(final int x, final int z) { + this.loadedChunks.remove(ChunkPosition.chunkKey(x, z)); + } + + public boolean isChunkLoaded(final int x, final int z) { + return this.loadedChunks.contains(ChunkPosition.chunkKey(x, z)); + } + + public void clear() { + this.loadedChunks.clear(); + } + +}