From 574b52a4ff6c2a721a625bb282997fde1f4454e6 Mon Sep 17 00:00:00 2001 From: FlorianMichael Date: Sat, 13 Sep 2025 01:09:07 +0200 Subject: [PATCH] Cancel sign editor open packet for invalid positions in 1.21.2->1.21 Modern clients will no longer require the position to be valid (they will just ignore the packet / throw a warning rather than crash on it). --- .../v1_21_2to1_21/Protocol1_21_2To1_21.java | 2 + .../BlockItemPacketRewriter1_21_2.java | 100 +++++++++++++++++- .../rewriter/EntityPacketRewriter1_21_2.java | 7 ++ .../v1_21_2to1_21/storage/SignStorage.java | 45 ++++++++ 4 files changed, 151 insertions(+), 3 deletions(-) create mode 100644 common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_2to1_21/storage/SignStorage.java diff --git a/common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_2to1_21/Protocol1_21_2To1_21.java b/common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_2to1_21/Protocol1_21_2To1_21.java index 620d2fe0..09c43a2a 100644 --- a/common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_2to1_21/Protocol1_21_2To1_21.java +++ b/common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_2to1_21/Protocol1_21_2To1_21.java @@ -29,6 +29,7 @@ import com.viaversion.viabackwards.protocol.v1_21_2to1_21.storage.ItemTagStorage; import com.viaversion.viabackwards.protocol.v1_21_2to1_21.storage.PlayerStorage; import com.viaversion.viabackwards.protocol.v1_21_2to1_21.storage.RecipeStorage; +import com.viaversion.viabackwards.protocol.v1_21_2to1_21.storage.SignStorage; import com.viaversion.viaversion.api.connection.UserConnection; import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_20_5; import com.viaversion.viaversion.api.minecraft.item.data.ChatType; @@ -159,6 +160,7 @@ public void init(final UserConnection user) { user.put(new ItemTagStorage()); user.put(new RecipeStorage(this)); user.put(new PlayerStorage()); + user.put(new SignStorage()); } @Override diff --git a/common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_2to1_21/rewriter/BlockItemPacketRewriter1_21_2.java b/common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_2to1_21/rewriter/BlockItemPacketRewriter1_21_2.java index f9924fc3..8920f30c 100644 --- a/common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_2to1_21/rewriter/BlockItemPacketRewriter1_21_2.java +++ b/common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_2to1_21/rewriter/BlockItemPacketRewriter1_21_2.java @@ -26,12 +26,20 @@ import com.viaversion.viabackwards.protocol.v1_21_2to1_21.Protocol1_21_2To1_21; import com.viaversion.viabackwards.protocol.v1_21_2to1_21.storage.InventoryStateIdStorage; import com.viaversion.viabackwards.protocol.v1_21_2to1_21.storage.RecipeStorage; +import com.viaversion.viabackwards.protocol.v1_21_2to1_21.storage.SignStorage; import com.viaversion.viaversion.api.connection.UserConnection; import com.viaversion.viaversion.api.data.MappingData; +import com.viaversion.viaversion.api.data.entity.EntityTracker; +import com.viaversion.viaversion.api.minecraft.BlockChangeRecord; +import com.viaversion.viaversion.api.minecraft.BlockPosition; import com.viaversion.viaversion.api.minecraft.Holder; import com.viaversion.viaversion.api.minecraft.HolderSet; import com.viaversion.viaversion.api.minecraft.Particle; import com.viaversion.viaversion.api.minecraft.SoundEvent; +import com.viaversion.viaversion.api.minecraft.chunks.Chunk; +import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection; +import com.viaversion.viaversion.api.minecraft.chunks.DataPalette; +import com.viaversion.viaversion.api.minecraft.chunks.PaletteType; import com.viaversion.viaversion.api.minecraft.data.StructuredDataContainer; import com.viaversion.viaversion.api.minecraft.data.StructuredDataKey; import com.viaversion.viaversion.api.minecraft.item.Item; @@ -73,10 +81,7 @@ public BlockItemPacketRewriter1_21_2(final Protocol1_21_2To1_21 protocol) { public void registerPackets() { final BlockRewriter blockRewriter = BlockRewriter.for1_20_2(protocol); blockRewriter.registerBlockEvent(ClientboundPackets1_21_2.BLOCK_EVENT); - blockRewriter.registerBlockUpdate(ClientboundPackets1_21_2.BLOCK_UPDATE); - blockRewriter.registerSectionBlocksUpdate1_20(ClientboundPackets1_21_2.SECTION_BLOCKS_UPDATE); blockRewriter.registerLevelEvent1_21(ClientboundPackets1_21_2.LEVEL_EVENT, 2001); - blockRewriter.registerLevelChunk1_19(ClientboundPackets1_21_2.LEVEL_CHUNK_WITH_LIGHT, ChunkType1_20_2::new); blockRewriter.registerBlockEntityData(ClientboundPackets1_21_2.BLOCK_ENTITY_DATA); registerAdvancements1_20_3(ClientboundPackets1_21_2.UPDATE_ADVANCEMENTS); @@ -84,6 +89,91 @@ public void registerPackets() { registerMerchantOffers1_20_5(ClientboundPackets1_21_2.MERCHANT_OFFERS); registerSetCreativeModeSlot(ServerboundPackets1_20_5.SET_CREATIVE_MODE_SLOT); + protocol.registerClientbound(ClientboundPackets1_21_2.LEVEL_CHUNK_WITH_LIGHT, wrapper -> { + final Chunk chunk = blockRewriter.handleChunk1_19(wrapper, ChunkType1_20_2::new); + blockRewriter.handleBlockEntities(null, chunk, wrapper.user()); + + final EntityTracker tracker = wrapper.user().getEntityTracker(Protocol1_21_2To1_21.class); + + final SignStorage storage = wrapper.user().get(SignStorage.class); + storage.removeSigns(chunk.getX(), chunk.getZ()); + + for (int i = 0; i < chunk.getSections().length; i++) { + final ChunkSection section = chunk.getSections()[i]; + + final DataPalette blockPalette = section.palette(PaletteType.BLOCKS); + + boolean containsSign = false; + for (int idx = 0; idx < blockPalette.size(); idx++) { + if (signBlockState(blockPalette.idByIndex(idx))) { + containsSign = true; + break; + } + } + + if (!containsSign) { + continue; + } + + for (int idx = 0; idx < ChunkSection.SIZE; idx++) { + if (!signBlockState(blockPalette.idAt(idx))) { + continue; + } + + storage.addSign(new BlockPosition( + ChunkSection.xFromIndex(idx) + (chunk.getX() << 4), + ChunkSection.yFromIndex(idx) + tracker.currentMinY() + (i << 4), + ChunkSection.zFromIndex(idx) + (chunk.getZ() << 4) + )); + } + } + }); + + protocol.registerClientbound(ClientboundPackets1_21_2.BLOCK_UPDATE, wrapper -> { + final SignStorage storage = wrapper.user().get(SignStorage.class); + final BlockPosition position = wrapper.passthrough(Types.BLOCK_POSITION1_14); + storage.removeSign(position); + + final int blockId = wrapper.read(Types.VAR_INT); + final int mappedBlockId = protocol.getMappingData().getNewBlockStateId(blockId); + wrapper.write(Types.VAR_INT, mappedBlockId); + + if (signBlockState(mappedBlockId)) { + storage.addSign(position); + } + }); + + protocol.registerClientbound(ClientboundPackets1_21_2.SECTION_BLOCKS_UPDATE, wrapper -> { + final long position = wrapper.passthrough(Types.LONG); + + final int chunkX = (int) (position >> 42); + final int chunkY = (int) (position << 44 >> 44); + final int chunkZ = (int) (position << 22 >> 42); + + final SignStorage signStorage = wrapper.user().get(SignStorage.class); + + for (final BlockChangeRecord record : wrapper.passthrough(Types.VAR_LONG_BLOCK_CHANGE_ARRAY)) { + record.setBlockId(protocol.getMappingData().getNewBlockStateId(record.getBlockId())); + + final int x = record.getSectionX() + (chunkX << 4); + final int y = record.getSectionY() + (chunkY << 4); + final int z = record.getSectionZ() + (chunkZ << 4); + final BlockPosition pos = new BlockPosition(x, y, z); + if (signBlockState(record.getBlockId())) { + signStorage.addSign(pos); + } else { + signStorage.removeSign(pos); + } + } + }); + + protocol.registerClientbound(ClientboundPackets1_21_2.OPEN_SIGN_EDITOR, wrapper -> { + final BlockPosition position = wrapper.passthrough(Types.BLOCK_POSITION1_14); + if (!wrapper.user().get(SignStorage.class).isSign(position)) { + wrapper.cancel(); + } + }); + protocol.registerClientbound(ClientboundPackets1_21_2.COOLDOWN, wrapper -> { final MappingData mappingData = protocol.getMappingData(); final String itemIdentifier = wrapper.read(Types.STRING); @@ -295,6 +385,10 @@ private void byteToVarInt(final PacketWrapper wrapper) { wrapper.write(Types.VAR_INT, (int) containerId); } + private boolean signBlockState(final int blockStateId) { + return (blockStateId >= 4302 && blockStateId <= 4589) || (blockStateId >= 4762 && blockStateId <= 5625); + } + @Override public Item handleItemToClient(final UserConnection connection, Item item) { backupInconvertibleData(item); diff --git a/common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_2to1_21/rewriter/EntityPacketRewriter1_21_2.java b/common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_2to1_21/rewriter/EntityPacketRewriter1_21_2.java index eb43088e..a337301e 100644 --- a/common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_2to1_21/rewriter/EntityPacketRewriter1_21_2.java +++ b/common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_2to1_21/rewriter/EntityPacketRewriter1_21_2.java @@ -26,6 +26,8 @@ import com.viaversion.viabackwards.api.rewriters.EntityRewriter; import com.viaversion.viabackwards.protocol.v1_21_2to1_21.Protocol1_21_2To1_21; import com.viaversion.viabackwards.protocol.v1_21_2to1_21.storage.PlayerStorage; +import com.viaversion.viabackwards.protocol.v1_21_2to1_21.storage.SignStorage; +import com.viaversion.viaversion.api.data.entity.EntityTracker; import com.viaversion.viaversion.api.minecraft.Holder; import com.viaversion.viaversion.api.minecraft.Particle; import com.viaversion.viaversion.api.minecraft.RegistryEntry; @@ -172,6 +174,11 @@ public void register() { wrapper.passthrough(Types.VAR_INT); // Portal cooldown wrapper.read(Types.VAR_INT); // Sea level + + final EntityTracker tracker = tracker(wrapper.user()); + if (tracker.currentWorld() != null && !tracker.currentWorld().equals(world)) { + wrapper.user().put(new SignStorage()); + } trackWorldDataByKey1_20_5(wrapper.user(), dimensionId, world); }); diff --git a/common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_2to1_21/storage/SignStorage.java b/common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_2to1_21/storage/SignStorage.java new file mode 100644 index 00000000..d0bc172f --- /dev/null +++ b/common/src/main/java/com/viaversion/viabackwards/protocol/v1_21_2to1_21/storage/SignStorage.java @@ -0,0 +1,45 @@ +/* + * This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards + * Copyright (C) 2016-2025 ViaVersion and contributors + * + * 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 . + */ +package com.viaversion.viabackwards.protocol.v1_21_2to1_21.storage; + +import com.viaversion.viaversion.api.connection.StorableObject; +import com.viaversion.viaversion.api.minecraft.BlockPosition; +import java.util.HashSet; +import java.util.Set; + +public final class SignStorage implements StorableObject { + + private final Set signs = new HashSet<>(); + + public void addSign(final BlockPosition position) { + signs.add(position); + } + + public boolean isSign(final BlockPosition position) { + return signs.contains(position); + } + + public void removeSign(final BlockPosition position) { + signs.remove(position); + } + + public void removeSigns(final int chunkX, final int chunkZ) { + signs.removeIf(pos -> pos.x() >> 4 == chunkX && pos.z() >> 4 == chunkZ); + } + +}