Skip to content

Commit 77d702b

Browse files
committed
Also handle movement related enchantments
Fixes ViaVersion#3960
1 parent c098e24 commit 77d702b

File tree

2 files changed

+115
-31
lines changed

2 files changed

+115
-31
lines changed

bukkit/src/main/java/com/viaversion/viaversion/bukkit/listeners/v1_20_5to1_21/PlayerChangeItemListener.java

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.bukkit.event.EventPriority;
3232
import org.bukkit.event.player.PlayerItemHeldEvent;
3333
import org.bukkit.inventory.ItemStack;
34+
import org.bukkit.inventory.PlayerInventory;
3435
import org.checkerframework.checker.nullness.qual.Nullable;
3536

3637
/**
@@ -40,6 +41,9 @@
4041
public final class PlayerChangeItemListener extends ViaBukkitListener {
4142

4243
private final Enchantment efficiency = Enchantment.getByKey(NamespacedKey.minecraft("efficiency"));
44+
private final Enchantment depthStrider = Enchantment.getByKey(NamespacedKey.minecraft("depth_strider"));
45+
private final Enchantment soulSpeed = Enchantment.getByKey(NamespacedKey.minecraft("soul_speed"));
46+
private final Enchantment swiftSneak = Enchantment.getByKey(NamespacedKey.minecraft("swift_sneak"));
4347

4448
public PlayerChangeItemListener(final ViaVersionPlugin plugin) {
4549
super(plugin, Protocol1_20_5To1_21.class);
@@ -49,19 +53,25 @@ public PlayerChangeItemListener(final ViaVersionPlugin plugin) {
4953
public void onPlayerInventorySlotChangedEvent(final PlayerInventorySlotChangeEvent event) {
5054
final Player player = event.getPlayer();
5155
final ItemStack item = event.getNewItemStack();
52-
if (event.getSlot() == player.getInventory().getHeldItemSlot()) {
53-
sendAttributeUpdate(player, item);
56+
final PlayerInventory inventory = player.getInventory();
57+
final int slot = event.getSlot();
58+
if (slot == inventory.getHeldItemSlot()) {
59+
sendAttributeUpdate(player, item, Slot.HAND);
60+
} else if (slot == 36) {
61+
sendAttributeUpdate(player, item, Slot.BOOTS);
62+
} else if (slot == 37) {
63+
sendAttributeUpdate(player, item, Slot.LEGGINGS);
5464
}
5565
}
5666

5767
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
5868
public void onPlayerItemHeld(final PlayerItemHeldEvent event) {
5969
final Player player = event.getPlayer();
6070
final ItemStack item = player.getInventory().getItem(event.getNewSlot());
61-
sendAttributeUpdate(player, item);
71+
sendAttributeUpdate(player, item, Slot.HAND);
6272
}
6373

64-
private void sendAttributeUpdate(final Player player, @Nullable final ItemStack item) {
74+
private void sendAttributeUpdate(final Player player, @Nullable final ItemStack item, final Slot slot) {
6575
final UserConnection connection = Via.getAPI().getConnection(player.getUniqueId());
6676
if (connection == null || !isOnPipe(player)) {
6777
return;
@@ -72,7 +82,23 @@ private void sendAttributeUpdate(final Player player, @Nullable final ItemStack
7282
return;
7383
}
7484

75-
final int efficiencyLevel = item != null ? item.getEnchantmentLevel(efficiency) : 0;
76-
storage.setEfficiencyLevel(new EfficiencyAttributeStorage.StoredEfficiency(player.getEntityId(), efficiencyLevel), connection);
85+
final EfficiencyAttributeStorage.ActiveEnchants activeEnchants = storage.activeEnchants();
86+
int efficiencyLevel = activeEnchants.efficiency().level();
87+
int soulSpeedLevel = activeEnchants.soulSpeed().level();
88+
int swiftSneakLevel = activeEnchants.swiftSneak().level();
89+
int depthStriderLevel = activeEnchants.depthStrider().level();
90+
switch (slot) {
91+
case HAND -> efficiencyLevel = item != null ? item.getEnchantmentLevel(efficiency) : 0;
92+
case LEGGINGS -> swiftSneakLevel = item != null && swiftSneak != null ? item.getEnchantmentLevel(swiftSneak) : 0;
93+
case BOOTS -> {
94+
depthStriderLevel = item != null && depthStrider != null ? item.getEnchantmentLevel(depthStrider) : 0;
95+
soulSpeedLevel = item != null && soulSpeed != null ? item.getEnchantmentLevel(soulSpeed) : 0;
96+
}
97+
}
98+
storage.setEnchants(player.getEntityId(), connection, efficiencyLevel, soulSpeedLevel, swiftSneakLevel, depthStriderLevel);
99+
}
100+
101+
private enum Slot {
102+
HAND, BOOTS, LEGGINGS
77103
}
78104
}

common/src/main/java/com/viaversion/viaversion/protocols/v1_20_5to1_21/storage/EfficiencyAttributeStorage.java

Lines changed: 83 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -23,57 +23,115 @@
2323
import com.viaversion.viaversion.api.type.Types;
2424
import com.viaversion.viaversion.protocols.v1_20_5to1_21.Protocol1_20_5To1_21;
2525
import com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundPackets1_21;
26+
import java.util.List;
2627

2728
public final class EfficiencyAttributeStorage implements StorableObject {
2829

29-
private static final int MINING_EFFICIENCY_ID = 19;
30-
private final Object lock = new Object(); // Slightly sloppy locking, but should be good enough
30+
public static final EnchantAttributeModifier EFFICIENCY = new EnchantAttributeModifier("minecraft:enchantment.efficiency/mainhand", 19, 0, level -> (level * level) + 1);
31+
public static final EnchantAttributeModifier SOUL_SPEED = new EnchantAttributeModifier("minecraft:enchantment.soul_speed", 21, 0.1, level -> 0.04D + ((level - 1) * 0.01D));
32+
public static final EnchantAttributeModifier SWIFT_SNEAK = new EnchantAttributeModifier("minecraft:enchantment.swift_sneak", 25, 0.3, level -> level * 0.15D);
33+
public static final EnchantAttributeModifier DEPTH_STRIDER = new EnchantAttributeModifier("minecraft:enchantment.depth_strider", 30, 0, level -> level / 3D);
34+
private static final ActiveEnchants DEFAULT = new ActiveEnchants(-1,
35+
new ActiveEnchant(EFFICIENCY, 0),
36+
new ActiveEnchant(SOUL_SPEED, 0),
37+
new ActiveEnchant(SWIFT_SNEAK, 0),
38+
new ActiveEnchant(DEPTH_STRIDER, 0)
39+
);
40+
private final Object lock = new Object();
41+
private volatile ActiveEnchants activeEnchants = DEFAULT;
42+
private volatile boolean attributesSent = true;
3143
private volatile boolean loginSent;
32-
private volatile StoredEfficiency efficiencyLevel;
3344

34-
public void setEfficiencyLevel(final StoredEfficiency efficiencyLevel, final UserConnection connection) {
35-
this.efficiencyLevel = efficiencyLevel;
45+
public void setEnchants(final int entityId, final UserConnection connection, final int efficiency, final int soulSpeed, final int swiftSneak, final int depthStrider) {
46+
// Always called from the main thread
47+
if (efficiency == activeEnchants.efficiency.level
48+
&& soulSpeed == activeEnchants.soulSpeed.level
49+
&& swiftSneak == activeEnchants.swiftSneak.level
50+
&& depthStrider == activeEnchants.depthStrider.level) {
51+
return;
52+
}
53+
54+
synchronized (lock) {
55+
this.activeEnchants = new ActiveEnchants(entityId,
56+
new ActiveEnchant(EFFICIENCY, efficiency),
57+
new ActiveEnchant(SOUL_SPEED, soulSpeed),
58+
new ActiveEnchant(SWIFT_SNEAK, swiftSneak),
59+
new ActiveEnchant(DEPTH_STRIDER, depthStrider)
60+
);
61+
this.attributesSent = false;
62+
}
3663
sendAttributesPacket(connection);
3764
}
3865

66+
public ActiveEnchants activeEnchants() {
67+
return activeEnchants;
68+
}
69+
3970
public void onLoginSent(final UserConnection connection) {
71+
// Always called from the netty thread
4072
this.loginSent = true;
4173
sendAttributesPacket(connection);
4274
}
4375

4476
private void sendAttributesPacket(final UserConnection connection) {
45-
final StoredEfficiency efficiency;
77+
final ActiveEnchants enchants;
4678
synchronized (lock) {
4779
// Older servers (and often Bungee) will send world state packets before sending the login packet
48-
if (!loginSent || efficiencyLevel == null) {
80+
if (!loginSent || attributesSent) {
4981
return;
5082
}
5183

52-
efficiency = efficiencyLevel;
53-
efficiencyLevel = null;
84+
enchants = this.activeEnchants;
85+
attributesSent = true;
5486
}
5587

5688
final PacketWrapper attributesPacket = PacketWrapper.create(ClientboundPackets1_21.UPDATE_ATTRIBUTES, connection);
57-
attributesPacket.write(Types.VAR_INT, efficiency.entityId());
58-
59-
attributesPacket.write(Types.VAR_INT, 1); // Size
60-
attributesPacket.write(Types.VAR_INT, MINING_EFFICIENCY_ID); // Attribute ID
61-
attributesPacket.write(Types.DOUBLE, 0D); // Base
62-
63-
final int level = efficiency.level;
64-
if (level > 0) {
65-
final double modifierAmount = (level * level) + 1D;
66-
attributesPacket.write(Types.VAR_INT, 1); // Modifiers
67-
attributesPacket.write(Types.STRING, "minecraft:enchantment.efficiency/mainhand"); // Id
68-
attributesPacket.write(Types.DOUBLE, modifierAmount);
69-
attributesPacket.write(Types.BYTE, (byte) 0); // 'Add' operation
70-
} else {
71-
attributesPacket.write(Types.VAR_INT, 0); // Modifiers
89+
attributesPacket.write(Types.VAR_INT, enchants.entityId());
90+
91+
final List<ActiveEnchant> list = List.of(enchants.efficiency(), enchants.soulSpeed(), enchants.swiftSneak(), enchants.depthStrider());
92+
attributesPacket.write(Types.VAR_INT, list.size());
93+
for (final ActiveEnchant enchant : list) {
94+
final EnchantAttributeModifier modifier = enchant.modifier;
95+
attributesPacket.write(Types.VAR_INT, modifier.attributeId);
96+
attributesPacket.write(Types.DOUBLE, modifier.baseValue);
97+
98+
if (enchant.level > 0) {
99+
attributesPacket.write(Types.VAR_INT, 1); // Modifiers
100+
attributesPacket.write(Types.STRING, modifier.key);
101+
attributesPacket.write(Types.DOUBLE, enchant.modifier.modifierFunction.get(enchant.level));
102+
attributesPacket.write(Types.BYTE, (byte) 0); // 'Add' operation
103+
} else {
104+
attributesPacket.write(Types.VAR_INT, 0); // Modifiers
105+
}
72106
}
73107

74108
attributesPacket.scheduleSend(Protocol1_20_5To1_21.class);
75109
}
76110

77-
public record StoredEfficiency(int entityId, int level) {
111+
public record ActiveEnchants(int entityId, ActiveEnchant efficiency, ActiveEnchant soulSpeed,
112+
ActiveEnchant swiftSneak, ActiveEnchant depthStrider) {
113+
}
114+
115+
public record ActiveEnchant(EnchantAttributeModifier modifier, int level) {
116+
}
117+
118+
public static final class EnchantAttributeModifier { // Private constructor, equals by reference
119+
private final String key;
120+
private final int attributeId;
121+
private final double baseValue;
122+
private final LevelToModifier modifierFunction;
123+
124+
private EnchantAttributeModifier(final String key, final int attributeId, final double baseValue, final LevelToModifier modifierFunction) {
125+
this.key = key;
126+
this.attributeId = attributeId;
127+
this.baseValue = baseValue;
128+
this.modifierFunction = modifierFunction;
129+
}
130+
}
131+
132+
@FunctionalInterface
133+
private interface LevelToModifier {
134+
135+
double get(int level);
78136
}
79137
}

0 commit comments

Comments
 (0)