PlayerInteractEvent
期间双重使用物品的问题上下文:我正在为 Minecraft 版本 1.16.5 开发一个插件,其中添加了一个特殊项目
FlashDust
,使用时会对一定半径内的玩家产生影响。在代码中,我注册了 PlayerInteractEvent
事件,并尝试通过检查 event.getHand()
将项目的使用限制为手动过滤。
问题:当玩家使用该物品时,
PlayerInteractEvent
事件连续触发两次,导致该物品被使用两次,即使我手动过滤事件。我尝试过使用 EquipmentSlot.HAND
插槽检查,但这并不能解决问题。
我怀疑主手和副手都触发了该事件,但手动检查没有帮助。
这是代码:
package so.max1soft.customitems.dusts;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.util.Vector;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.Particle;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class FlashDust implements Listener {
private final JavaPlugin plugin;
private final Map<Player, Long> lastUse = new HashMap<>();
public FlashDust(JavaPlugin plugin) {
this.plugin = plugin;
}
public static void register(JavaPlugin plugin) {
plugin.getServer().getPluginManager().registerEvents(new FlashDust(plugin), plugin);
}
public static ItemStack createItem(JavaPlugin plugin) {
FileConfiguration config = plugin.getConfig();
ItemStack item = new ItemStack(Material.SUGAR);
ItemMeta meta = item.getItemMeta();
if (meta == null) {
return item;
}
String name = config.getString("dusts.flash.name");
List<String> lore = config.getStringList("dusts.flash.lore");
meta.setDisplayName(name);
meta.setLore(lore);
List<String> enchantments = config.getStringList("dusts.flash.enchantments");
for (String enchantString : enchantments) {
String[] parts = enchantString.split(":");
if (parts.length != 2) {
plugin.getLogger().warning("Invalid enchantment format: " + enchantString);
continue;
}
String enchantmentName = parts[0].toUpperCase();
Enchantment enchantment = Enchantment.getByKey(NamespacedKey.minecraft(enchantmentName.toLowerCase()));
if (enchantment == null) {
plugin.getLogger().warning("Unknown enchantment: " + enchantmentName);
continue;
}
try {
int level = Integer.parseInt(parts[1]);
meta.addEnchant(enchantment, level, true);
} catch (NumberFormatException e) {
plugin.getLogger().warning("Invalid enchantment level: " + parts[1] + " for enchantment " + enchantmentName);
}
}
meta.addItemFlags(ItemFlag.HIDE_ENCHANTS);
item.setItemMeta(meta);
return item;
}
@EventHandler
public void onPlayerUseFlasDust(PlayerInteractEvent event) {
if (event.getHand() == EquipmentSlot.HAND) {
if (event.getAction() != Action.RIGHT_CLICK_AIR && event.getAction() != Action.RIGHT_CLICK_BLOCK) {
return;
}
Player player = event.getPlayer();
ItemStack item = event.getItem();
if (item == null || item.getType() != Material.SUGAR) {
return;
}
ItemMeta meta = item.getItemMeta();
if (meta == null || !meta.hasDisplayName() || !meta.getDisplayName().equals(plugin.getConfig().getString("dusts.flash.name"))) {
return;
}
if (lastUse.containsKey(player) && lastUse.get(player) + 1000L > System.currentTimeMillis()) {
return;
}
if (!isCooldownActive(player)) {
player.sendMessage("You are on cooldown!");
return;
}
setCooldown(player);
event.setCancelled(true);
processFlasDustUsage(player, item);
}
}
private boolean isCooldownActive(Player player) {
Long lastInteract = lastUse.get(player);
if (lastInteract == null) {
return true;
}
return System.currentTimeMillis() - lastInteract >= 20000L;
}
private void setCooldown(Player player) {
lastUse.put(player, System.currentTimeMillis());
}
private void processFlasDustUsage(Player player, ItemStack item) {
FileConfiguration config = plugin.getConfig();
String itemName = config.getString("dusts.flash.name");
List<String> expectedLore = config.getStringList("dusts.flash.lore");
ItemMeta meta = item.getItemMeta();
if (!meta.getDisplayName().equals(itemName) || !meta.hasLore() || !meta.getLore().equals(expectedLore)) {
plugin.getLogger().info("Item does not match expected name or lore, cancelling event.");
return;
}
if (item.getAmount() > 1) {
item.setAmount(item.getAmount() - 1);
} else {
player.getInventory().remove(item);
}
applyEffectsAndParticles(player);
}
private void applyEffectsAndParticles(Player player) {
Vector initialLocation = player.getLocation().toVector();
double fixedY = player.getLocation().getY();
new BukkitRunnable() {
double radius = 0;
final double maxRadius = 7;
final double step = 0.5;
@Override
public void run() {
radius += step;
if (radius > maxRadius) {
cancel();
return;
}
for (double angle = 0; angle < 360; angle += 10) {
double radians = Math.toRadians(angle);
double x = radius * Math.cos(radians);
double z = radius * Math.sin(radians);
Vector particleLocation = initialLocation.clone().add(new Vector(x, 0, z));
player.getWorld().spawnParticle(Particle.CLOUD, particleLocation.getX(), fixedY + 0.5, particleLocation.getZ(), 1, 0, 0, 0, 0);
}
for (Player nearbyPlayer : player.getWorld().getPlayers()) {
if (nearbyPlayer.equals(player)) continue;
if (nearbyPlayer.getLocation().distance(initialLocation.toLocation(player.getWorld())) <= radius) {
if (!nearbyPlayer.hasPotionEffect(PotionEffectType.BLINDNESS)) {
nearbyPlayer.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, 7 * 20, 0, false, false));
nearbyPlayer.addPotionEffect(new PotionEffect(PotionEffectType.POISON, 3 * 20, 0, false, false));
nearbyPlayer.addPotionEffect(new PotionEffect(PotionEffectType.SLOW, 7 * 20, 0, false, false));
nearbyPlayer.addPotionEffect(new PotionEffect(PotionEffectType.GLOWING, 7 * 20, 0, false, false));
}
}
}
}
}.runTaskTimer(plugin, 0, 2);
}
}
Main.java 类
public class Main extends JavaPlugin {
@Override
public void onEnable() {
saveDefaultConfig();
FileConfiguration config = getConfig();
// Отладочная информация
getLogger().info("Спавн Брейкер шанс: " + config.getDouble("spawnbreak.chance"));
FlashDust.register(this);
ZenithDust.register(this);
RadiusPickaxe.register(this);
AutoSmeltingPickaxe.register(this);
MagnetPickaxe.register(this);
CombinedPickaxe.register(this);
AutoSmeltingShovel.register(this);
SpawnerBreaker.register(this);
getCommand("socustomitems").setExecutor(this);
getCommand("socustomitems").setTabCompleter(new CustomTabCompleter());
}
@Override
public void onDisable() {
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (command.getName().equalsIgnoreCase("socustomitems") && args.length == 3 && args[0].equalsIgnoreCase("give")) {
if (sender.hasPermission("socustomitems.give")) {
Player target = getServer().getPlayer(args[1]);
if (target != null) {
String pickaxeType = args[2];
if (pickaxeType.equalsIgnoreCase("radius")) {
target.getInventory().addItem(RadiusPickaxe.createItem(this));
} else if (pickaxeType.equalsIgnoreCase("flame")) {
target.getInventory().addItem(AutoSmeltingPickaxe.createItem(this));
} else if (pickaxeType.equalsIgnoreCase("magnet")) {
target.getInventory().addItem(MagnetPickaxe.createItem(this));
} else if (pickaxeType.equalsIgnoreCase("combo")) {
target.getInventory().addItem(CombinedPickaxe.createItem(this));
} else if (pickaxeType.equalsIgnoreCase("flameshovel")) {
target.getInventory().addItem(AutoSmeltingShovel.createItem(this));
} else if (pickaxeType.equalsIgnoreCase("spawnbreaker")) {
target.getInventory().addItem(SpawnerBreaker.createItem(this));
} else if (pickaxeType.equalsIgnoreCase("flashdust")) {
target.getInventory().addItem(FlashDust.createItem(this));
} else if (pickaxeType.equalsIgnoreCase("zenithdust")) {
target.getInventory().addItem(ZenithDust.createItem(this));
} else {
sender.sendMessage("Типы: radius, flame, magnet, combo, flameshovel, spawnbreaker, flashdust, zenithdust");
}
return true;
} else {
sender.sendMessage("Такого кубхеда не существует!");
}
}
}
return false;
}
}
您在手牌检查中嵌套了另一个条件,这意味着只有两个条件都为真时它才会返回。你检查这手牌的条件可以重构为:
if (event.getHand() == EquipmentSlot.HAND && (event.getAction() == Action.RIGHT_CLICK_AIR || event.getAction() == Action.RIGHT_CLICK_BLOCK)) {
return
}
这意味着手必须是
HAND
并且动作必须是RIGHT_CLICK_AIR
或RIGHT_CLICK_BLOCK
才能返回。您可以拆分这些条件或对其进行修改,以便它们按您的预期工作。
拆分逻辑:
if (event.getHand() != EquipmentSlot.HAND) {
// Return if the hand isn't HAND (e.g. OFF_HAND)
return;
}
// Continue the rest of our logic
if (event.getAction()...
或者正确加入逻辑:
if (event.getHand() != EquipmentSlot.HAND || event.getAction() == Action.RIGHT_CLICK_AIR || event.getAction() == Action.RIGHT_CLICK_BLOCK) {
return
}