diff --git a/bukkit/src/main/java/com/viaversion/viaversion/bukkit/listeners/v1_20_5to1_21/PlayerChangeItemListener.java b/bukkit/src/main/java/com/viaversion/viaversion/bukkit/listeners/v1_20_5to1_21/PlayerChangeItemListener.java index deb56697f..03576280e 100644 --- a/bukkit/src/main/java/com/viaversion/viaversion/bukkit/listeners/v1_20_5to1_21/PlayerChangeItemListener.java +++ b/bukkit/src/main/java/com/viaversion/viaversion/bukkit/listeners/v1_20_5to1_21/PlayerChangeItemListener.java @@ -20,11 +20,9 @@ package com.viaversion.viaversion.bukkit.listeners.v1_20_5to1_21; import com.viaversion.viaversion.ViaVersionPlugin; import com.viaversion.viaversion.api.Via; import com.viaversion.viaversion.api.connection.UserConnection; -import com.viaversion.viaversion.api.protocol.packet.PacketWrapper; -import com.viaversion.viaversion.api.type.Types; import com.viaversion.viaversion.bukkit.listeners.ViaBukkitListener; import com.viaversion.viaversion.protocols.v1_20_5to1_21.Protocol1_20_5To1_21; -import com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundPackets1_21; +import com.viaversion.viaversion.protocols.v1_20_5to1_21.storage.EfficiencyAttributeStorage; import io.papermc.paper.event.player.PlayerInventorySlotChangeEvent; import org.bukkit.NamespacedKey; import org.bukkit.enchantments.Enchantment; @@ -41,7 +39,6 @@ import org.checkerframework.checker.nullness.qual.Nullable; */ public final class PlayerChangeItemListener extends ViaBukkitListener { - private static final int MINING_EFFICIENCY_ID = 19; private final Enchantment efficiency = Enchantment.getByKey(NamespacedKey.minecraft("efficiency")); public PlayerChangeItemListener(final ViaVersionPlugin plugin) { @@ -70,24 +67,12 @@ public final class PlayerChangeItemListener extends ViaBukkitListener { return; } - final int efficiencyLevel = item != null ? item.getEnchantmentLevel(efficiency) : 0; - final PacketWrapper attributesPacket = PacketWrapper.create(ClientboundPackets1_21.UPDATE_ATTRIBUTES, connection); - attributesPacket.write(Types.VAR_INT, player.getEntityId()); - - attributesPacket.write(Types.VAR_INT, 1); // Size - attributesPacket.write(Types.VAR_INT, MINING_EFFICIENCY_ID); // Attribute ID - attributesPacket.write(Types.DOUBLE, 0D); // Base - - if (efficiencyLevel > 0) { - final double modifierAmount = (efficiencyLevel * efficiencyLevel) + 1D; - attributesPacket.write(Types.VAR_INT, 1); // Modifiers - attributesPacket.write(Types.STRING, "minecraft:enchantment.efficiency/mainhand"); // Id - attributesPacket.write(Types.DOUBLE, modifierAmount); - attributesPacket.write(Types.BYTE, (byte) 0); // 'Add' operation - } else { - attributesPacket.write(Types.VAR_INT, 0); // Modifiers + final EfficiencyAttributeStorage storage = connection.get(EfficiencyAttributeStorage.class); + if (storage == null) { + return; } - attributesPacket.scheduleSend(Protocol1_20_5To1_21.class); + final int efficiencyLevel = item != null ? item.getEnchantmentLevel(efficiency) : 0; + storage.setEfficiencyLevel(new EfficiencyAttributeStorage.StoredEfficiency(player.getEntityId(), efficiencyLevel), connection); } } diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_20_5to1_21/Protocol1_20_5To1_21.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_20_5to1_21/Protocol1_20_5To1_21.java index 7bb46567d..683131b42 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_20_5to1_21/Protocol1_20_5To1_21.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_20_5to1_21/Protocol1_20_5To1_21.java @@ -44,6 +44,7 @@ import com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundPacke import com.viaversion.viaversion.protocols.v1_20_5to1_21.rewriter.BlockItemPacketRewriter1_21; import com.viaversion.viaversion.protocols.v1_20_5to1_21.rewriter.ComponentRewriter1_21; import com.viaversion.viaversion.protocols.v1_20_5to1_21.rewriter.EntityPacketRewriter1_21; +import com.viaversion.viaversion.protocols.v1_20_5to1_21.storage.EfficiencyAttributeStorage; import com.viaversion.viaversion.rewriter.ComponentRewriter; import com.viaversion.viaversion.rewriter.SoundRewriter; import com.viaversion.viaversion.rewriter.StatisticsRewriter; @@ -220,6 +221,7 @@ public final class Protocol1_20_5To1_21 extends AbstractProtocol wrapper.user().get(EfficiencyAttributeStorage.class).onLoginSent(wrapper.user())); } }); diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_20_5to1_21/storage/EfficiencyAttributeStorage.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_20_5to1_21/storage/EfficiencyAttributeStorage.java new file mode 100644 index 000000000..8b9c5cd16 --- /dev/null +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_20_5to1_21/storage/EfficiencyAttributeStorage.java @@ -0,0 +1,79 @@ +/* + * This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion + * Copyright (C) 2016-2024 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.viaversion.protocols.v1_20_5to1_21.storage; + +import com.viaversion.viaversion.api.connection.StorableObject; +import com.viaversion.viaversion.api.connection.UserConnection; +import com.viaversion.viaversion.api.protocol.packet.PacketWrapper; +import com.viaversion.viaversion.api.type.Types; +import com.viaversion.viaversion.protocols.v1_20_5to1_21.Protocol1_20_5To1_21; +import com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundPackets1_21; + +public final class EfficiencyAttributeStorage implements StorableObject { + + private static final int MINING_EFFICIENCY_ID = 19; + private final Object lock = new Object(); // Slightly sloppy locking, but should be good enough + private volatile boolean loginSent; + private volatile StoredEfficiency efficiencyLevel; + + public void setEfficiencyLevel(final StoredEfficiency efficiencyLevel, final UserConnection connection) { + this.efficiencyLevel = efficiencyLevel; + sendAttributesPacket(connection); + } + + public void onLoginSent(final UserConnection connection) { + this.loginSent = true; + sendAttributesPacket(connection); + } + + private void sendAttributesPacket(final UserConnection connection) { + final StoredEfficiency efficiency; + synchronized (lock) { + // Older servers (and often Bungee) will send world state packets before sending the login packet + if (!loginSent || efficiencyLevel == null) { + return; + } + + efficiency = efficiencyLevel; + efficiencyLevel = null; + } + + final PacketWrapper attributesPacket = PacketWrapper.create(ClientboundPackets1_21.UPDATE_ATTRIBUTES, connection); + attributesPacket.write(Types.VAR_INT, efficiency.entityId()); + + attributesPacket.write(Types.VAR_INT, 1); // Size + attributesPacket.write(Types.VAR_INT, MINING_EFFICIENCY_ID); // Attribute ID + attributesPacket.write(Types.DOUBLE, 0D); // Base + + final int level = efficiency.level; + if (level > 0) { + final double modifierAmount = (level * level) + 1D; + attributesPacket.write(Types.VAR_INT, 1); // Modifiers + attributesPacket.write(Types.STRING, "minecraft:enchantment.efficiency/mainhand"); // Id + attributesPacket.write(Types.DOUBLE, modifierAmount); + attributesPacket.write(Types.BYTE, (byte) 0); // 'Add' operation + } else { + attributesPacket.write(Types.VAR_INT, 0); // Modifiers + } + + attributesPacket.scheduleSend(Protocol1_20_5To1_21.class); + } + + public record StoredEfficiency(int entityId, int level) { + } +}