3
0
Mirror von https://github.com/ViaVersion/ViaVersion.git synchronisiert 2024-11-19 14:30:16 +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.EventPriority;
import org.bukkit.event.player.PlayerItemHeldEvent; import org.bukkit.event.player.PlayerItemHeldEvent;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
/** /**
@ -40,6 +41,9 @@ import org.checkerframework.checker.nullness.qual.Nullable;
public final class PlayerChangeItemListener extends ViaBukkitListener { public final class PlayerChangeItemListener extends ViaBukkitListener {
private final Enchantment efficiency = Enchantment.getByKey(NamespacedKey.minecraft("efficiency")); 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) { public PlayerChangeItemListener(final ViaVersionPlugin plugin) {
super(plugin, Protocol1_20_5To1_21.class); super(plugin, Protocol1_20_5To1_21.class);
@ -49,8 +53,14 @@ public final class PlayerChangeItemListener extends ViaBukkitListener {
public void onPlayerInventorySlotChangedEvent(final PlayerInventorySlotChangeEvent event) { public void onPlayerInventorySlotChangedEvent(final PlayerInventorySlotChangeEvent event) {
final Player player = event.getPlayer(); final Player player = event.getPlayer();
final ItemStack item = event.getNewItemStack(); final ItemStack item = event.getNewItemStack();
if (event.getSlot() == player.getInventory().getHeldItemSlot()) { final PlayerInventory inventory = player.getInventory();
sendAttributeUpdate(player, item); 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) { public void onPlayerItemHeld(final PlayerItemHeldEvent event) {
final Player player = event.getPlayer(); final Player player = event.getPlayer();
final ItemStack item = player.getInventory().getItem(event.getNewSlot()); 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()); final UserConnection connection = Via.getAPI().getConnection(player.getUniqueId());
if (connection == null || !isOnPipe(player)) { if (connection == null || !isOnPipe(player)) {
return; return;
@ -72,7 +82,23 @@ public final class PlayerChangeItemListener extends ViaBukkitListener {
return; return;
} }
final int efficiencyLevel = item != null ? item.getEnchantmentLevel(efficiency) : 0; final EfficiencyAttributeStorage.ActiveEnchants activeEnchants = storage.activeEnchants();
storage.setEfficiencyLevel(new EfficiencyAttributeStorage.StoredEfficiency(player.getEntityId(), efficiencyLevel), connection); 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.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.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.packet.ClientboundPackets1_21;
import java.util.List;
public final class EfficiencyAttributeStorage implements StorableObject { public final class EfficiencyAttributeStorage implements StorableObject {
private static final int MINING_EFFICIENCY_ID = 19; public static final EnchantAttributeModifier EFFICIENCY = new EnchantAttributeModifier("minecraft:enchantment.efficiency/mainhand", 19, 0, level -> (level * level) + 1);
private final Object lock = new Object(); // Slightly sloppy locking, but should be good enough 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 boolean loginSent;
private volatile StoredEfficiency efficiencyLevel;
public void setEfficiencyLevel(final StoredEfficiency efficiencyLevel, final UserConnection connection) { public void setEnchants(final int entityId, final UserConnection connection, final int efficiency, final int soulSpeed, final int swiftSneak, final int depthStrider) {
this.efficiencyLevel = efficiencyLevel; // 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); sendAttributesPacket(connection);
} }
public ActiveEnchants activeEnchants() {
return activeEnchants;
}
public void onLoginSent(final UserConnection connection) { public void onLoginSent(final UserConnection connection) {
// Always called from the netty thread
this.loginSent = true; this.loginSent = true;
sendAttributesPacket(connection); sendAttributesPacket(connection);
} }
private void sendAttributesPacket(final UserConnection connection) { private void sendAttributesPacket(final UserConnection connection) {
final StoredEfficiency efficiency; final ActiveEnchants enchants;
synchronized (lock) { synchronized (lock) {
// Older servers (and often Bungee) will send world state packets before sending the login packet // Older servers (and often Bungee) will send world state packets before sending the login packet
if (!loginSent || efficiencyLevel == null) { if (!loginSent || attributesSent) {
return; return;
} }
efficiency = efficiencyLevel; enchants = this.activeEnchants;
efficiencyLevel = null; attributesSent = true;
} }
final PacketWrapper attributesPacket = PacketWrapper.create(ClientboundPackets1_21.UPDATE_ATTRIBUTES, connection); 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 final List<ActiveEnchant> list = List.of(enchants.efficiency(), enchants.soulSpeed(), enchants.swiftSneak(), enchants.depthStrider());
attributesPacket.write(Types.VAR_INT, MINING_EFFICIENCY_ID); // Attribute ID attributesPacket.write(Types.VAR_INT, list.size());
attributesPacket.write(Types.DOUBLE, 0D); // Base 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 (enchant.level > 0) {
if (level > 0) { attributesPacket.write(Types.VAR_INT, 1); // Modifiers
final double modifierAmount = (level * level) + 1D; attributesPacket.write(Types.STRING, modifier.key);
attributesPacket.write(Types.VAR_INT, 1); // Modifiers attributesPacket.write(Types.DOUBLE, enchant.modifier.modifierFunction.get(enchant.level));
attributesPacket.write(Types.STRING, "minecraft:enchantment.efficiency/mainhand"); // Id attributesPacket.write(Types.BYTE, (byte) 0); // 'Add' operation
attributesPacket.write(Types.DOUBLE, modifierAmount); } else {
attributesPacket.write(Types.BYTE, (byte) 0); // 'Add' operation attributesPacket.write(Types.VAR_INT, 0); // Modifiers
} else { }
attributesPacket.write(Types.VAR_INT, 0); // Modifiers
} }
attributesPacket.scheduleSend(Protocol1_20_5To1_21.class); 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);
} }
} }