3
0
Mirror von https://github.com/ViaVersion/ViaVersion.git synchronisiert 2024-12-25 15:50:10 +01:00

Also handle movement related enchantments

Fixes #3960
Dieser Commit ist enthalten in:
Nassim Jahnke 2024-06-19 23:57:22 +02:00
Ursprung c098e24c4f
Commit 77d702bc9b
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: EF6771C01F6EF02F
2 geänderte Dateien mit 113 neuen und 29 gelöschten Zeilen

Datei anzeigen

@ -31,6 +31,7 @@ import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.player.PlayerItemHeldEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
@ -40,6 +41,9 @@ import org.checkerframework.checker.nullness.qual.Nullable;
public final class PlayerChangeItemListener extends ViaBukkitListener {
private final Enchantment efficiency = Enchantment.getByKey(NamespacedKey.minecraft("efficiency"));
private final Enchantment depthStrider = Enchantment.getByKey(NamespacedKey.minecraft("depth_strider"));
private final Enchantment soulSpeed = Enchantment.getByKey(NamespacedKey.minecraft("soul_speed"));
private final Enchantment swiftSneak = Enchantment.getByKey(NamespacedKey.minecraft("swift_sneak"));
public PlayerChangeItemListener(final ViaVersionPlugin plugin) {
super(plugin, Protocol1_20_5To1_21.class);
@ -49,8 +53,14 @@ public final class PlayerChangeItemListener extends ViaBukkitListener {
public void onPlayerInventorySlotChangedEvent(final PlayerInventorySlotChangeEvent event) {
final Player player = event.getPlayer();
final ItemStack item = event.getNewItemStack();
if (event.getSlot() == player.getInventory().getHeldItemSlot()) {
sendAttributeUpdate(player, item);
final PlayerInventory inventory = player.getInventory();
final int slot = event.getSlot();
if (slot == inventory.getHeldItemSlot()) {
sendAttributeUpdate(player, item, Slot.HAND);
} else if (slot == 36) {
sendAttributeUpdate(player, item, Slot.BOOTS);
} else if (slot == 37) {
sendAttributeUpdate(player, item, Slot.LEGGINGS);
}
}
@ -58,10 +68,10 @@ public final class PlayerChangeItemListener extends ViaBukkitListener {
public void onPlayerItemHeld(final PlayerItemHeldEvent event) {
final Player player = event.getPlayer();
final ItemStack item = player.getInventory().getItem(event.getNewSlot());
sendAttributeUpdate(player, item);
sendAttributeUpdate(player, item, Slot.HAND);
}
private void sendAttributeUpdate(final Player player, @Nullable final ItemStack item) {
private void sendAttributeUpdate(final Player player, @Nullable final ItemStack item, final Slot slot) {
final UserConnection connection = Via.getAPI().getConnection(player.getUniqueId());
if (connection == null || !isOnPipe(player)) {
return;
@ -72,7 +82,23 @@ public final class PlayerChangeItemListener extends ViaBukkitListener {
return;
}
final int efficiencyLevel = item != null ? item.getEnchantmentLevel(efficiency) : 0;
storage.setEfficiencyLevel(new EfficiencyAttributeStorage.StoredEfficiency(player.getEntityId(), efficiencyLevel), connection);
final EfficiencyAttributeStorage.ActiveEnchants activeEnchants = storage.activeEnchants();
int efficiencyLevel = activeEnchants.efficiency().level();
int soulSpeedLevel = activeEnchants.soulSpeed().level();
int swiftSneakLevel = activeEnchants.swiftSneak().level();
int depthStriderLevel = activeEnchants.depthStrider().level();
switch (slot) {
case HAND -> efficiencyLevel = item != null ? item.getEnchantmentLevel(efficiency) : 0;
case LEGGINGS -> swiftSneakLevel = item != null && swiftSneak != null ? item.getEnchantmentLevel(swiftSneak) : 0;
case BOOTS -> {
depthStriderLevel = item != null && depthStrider != null ? item.getEnchantmentLevel(depthStrider) : 0;
soulSpeedLevel = item != null && soulSpeed != null ? item.getEnchantmentLevel(soulSpeed) : 0;
}
}
storage.setEnchants(player.getEntityId(), connection, efficiencyLevel, soulSpeedLevel, swiftSneakLevel, depthStriderLevel);
}
private enum Slot {
HAND, BOOTS, LEGGINGS
}
}

Datei anzeigen

@ -23,57 +23,115 @@ 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;
import java.util.List;
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
public static final EnchantAttributeModifier EFFICIENCY = new EnchantAttributeModifier("minecraft:enchantment.efficiency/mainhand", 19, 0, level -> (level * level) + 1);
public static final EnchantAttributeModifier SOUL_SPEED = new EnchantAttributeModifier("minecraft:enchantment.soul_speed", 21, 0.1, level -> 0.04D + ((level - 1) * 0.01D));
public static final EnchantAttributeModifier SWIFT_SNEAK = new EnchantAttributeModifier("minecraft:enchantment.swift_sneak", 25, 0.3, level -> level * 0.15D);
public static final EnchantAttributeModifier DEPTH_STRIDER = new EnchantAttributeModifier("minecraft:enchantment.depth_strider", 30, 0, level -> level / 3D);
private static final ActiveEnchants DEFAULT = new ActiveEnchants(-1,
new ActiveEnchant(EFFICIENCY, 0),
new ActiveEnchant(SOUL_SPEED, 0),
new ActiveEnchant(SWIFT_SNEAK, 0),
new ActiveEnchant(DEPTH_STRIDER, 0)
);
private final Object lock = new Object();
private volatile ActiveEnchants activeEnchants = DEFAULT;
private volatile boolean attributesSent = true;
private volatile boolean loginSent;
private volatile StoredEfficiency efficiencyLevel;
public void setEfficiencyLevel(final StoredEfficiency efficiencyLevel, final UserConnection connection) {
this.efficiencyLevel = efficiencyLevel;
public void setEnchants(final int entityId, final UserConnection connection, final int efficiency, final int soulSpeed, final int swiftSneak, final int depthStrider) {
// Always called from the main thread
if (efficiency == activeEnchants.efficiency.level
&& soulSpeed == activeEnchants.soulSpeed.level
&& swiftSneak == activeEnchants.swiftSneak.level
&& depthStrider == activeEnchants.depthStrider.level) {
return;
}
synchronized (lock) {
this.activeEnchants = new ActiveEnchants(entityId,
new ActiveEnchant(EFFICIENCY, efficiency),
new ActiveEnchant(SOUL_SPEED, soulSpeed),
new ActiveEnchant(SWIFT_SNEAK, swiftSneak),
new ActiveEnchant(DEPTH_STRIDER, depthStrider)
);
this.attributesSent = false;
}
sendAttributesPacket(connection);
}
public ActiveEnchants activeEnchants() {
return activeEnchants;
}
public void onLoginSent(final UserConnection connection) {
// Always called from the netty thread
this.loginSent = true;
sendAttributesPacket(connection);
}
private void sendAttributesPacket(final UserConnection connection) {
final StoredEfficiency efficiency;
final ActiveEnchants enchants;
synchronized (lock) {
// Older servers (and often Bungee) will send world state packets before sending the login packet
if (!loginSent || efficiencyLevel == null) {
if (!loginSent || attributesSent) {
return;
}
efficiency = efficiencyLevel;
efficiencyLevel = null;
enchants = this.activeEnchants;
attributesSent = true;
}
final PacketWrapper attributesPacket = PacketWrapper.create(ClientboundPackets1_21.UPDATE_ATTRIBUTES, connection);
attributesPacket.write(Types.VAR_INT, efficiency.entityId());
attributesPacket.write(Types.VAR_INT, enchants.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 List<ActiveEnchant> list = List.of(enchants.efficiency(), enchants.soulSpeed(), enchants.swiftSneak(), enchants.depthStrider());
attributesPacket.write(Types.VAR_INT, list.size());
for (final ActiveEnchant enchant : list) {
final EnchantAttributeModifier modifier = enchant.modifier;
attributesPacket.write(Types.VAR_INT, modifier.attributeId);
attributesPacket.write(Types.DOUBLE, modifier.baseValue);
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
if (enchant.level > 0) {
attributesPacket.write(Types.VAR_INT, 1); // Modifiers
attributesPacket.write(Types.STRING, modifier.key);
attributesPacket.write(Types.DOUBLE, enchant.modifier.modifierFunction.get(enchant.level));
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) {
public record ActiveEnchants(int entityId, ActiveEnchant efficiency, ActiveEnchant soulSpeed,
ActiveEnchant swiftSneak, ActiveEnchant depthStrider) {
}
public record ActiveEnchant(EnchantAttributeModifier modifier, int level) {
}
public static final class EnchantAttributeModifier { // Private constructor, equals by reference
private final String key;
private final int attributeId;
private final double baseValue;
private final LevelToModifier modifierFunction;
private EnchantAttributeModifier(final String key, final int attributeId, final double baseValue, final LevelToModifier modifierFunction) {
this.key = key;
this.attributeId = attributeId;
this.baseValue = baseValue;
this.modifierFunction = modifierFunction;
}
}
@FunctionalInterface
private interface LevelToModifier {
double get(int level);
}
}