You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Bukkit中创建同一物品独立实例:火焰发射器燃料共享问题

问题原因

你的代码核心问题有两个:

  1. 监听器与武器实例耦合:你把FlameLauncher同时作为Listener,如果只注册一个该类实例到Bukkit,所有物品的右键操作都会修改这同一个实例的fuel属性,导致多物品共享数据。
  2. 自定义数据存储错误:把fuel存在类成员变量里,而不是每个ItemStack的独立存储中,多个物品自然会互相干扰。
解决方案

1. 拆分监听器与武器定义

把武器的创建逻辑和事件监听逻辑分开,单独写一个WeaponListener类处理所有武器的交互事件,FlameLauncher只作为武器的模板类,负责生成带自定义数据的物品。

2. 用ItemMeta的隐藏Lore存储燃料数据

因为你使用的是Bukkit 1.8版本(不支持PersistentDataContainer),我们可以在物品的Lore中添加一行隐藏的燃料数据,用特殊颜色标记防止玩家修改和看到。


修改后的代码示例

第一步:重构FlameLauncher为武器生成类,移除Listener接口

package fr.laponie.ShooterPlugin;

import java.util.Arrays;
import java.util.List;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;

public class FlameLauncher implements Weapon {
    
    // 生成火焰发射器物品的静态方法,每次调用都创建独立实例
    public static ItemStack createFlameLauncher() {
        ItemStack is = new ItemStack(Material.GOLD_BARDING);
        ItemMeta im = is.getItemMeta();
        
        // 设置显示名称和基础Lore
        im.setDisplayName(getDisplayNameByFuel(10.0));
        List<String> lore = Arrays.asList(
            "This Weapon",
            "produces flames, igniting",
            "the target",
            ChatColor.MAGIC + "Fuel:" + 10.0 // 隐藏燃料数据,用特殊颜色隐藏
        );
        im.setLore(lore);
        
        is.setItemMeta(im);
        return is;
    }
    
    // 根据燃料值生成对应显示名称
    public static String getDisplayNameByFuel(double fuel) {
        if (fuel >= 8.0) {
            return "FLAME LAUNCHER [" + ChatColor.GREEN + "▪ ▪ ▪ ▪ ▪" + ChatColor.WHITE + "]";
        } else if (fuel >= 6.0) {
            return "FLAME LAUNCHER [" + ChatColor.GREEN + "▪ ▪ ▪ ▪" + ChatColor.WHITE + " ▪]";
        } else if (fuel >= 4.0) {
            return "FLAME LAUNCHER [" + ChatColor.GREEN + "▪ ▪ ▪" + ChatColor.WHITE + " ▪ ▪]";
        } else if (fuel >= 2.0) {
            return "FLAME LAUNCHER [" + ChatColor.GREEN + "▪ ▪" + ChatColor.WHITE + " ▪ ▪ ▪]";
        } else if (fuel > 0) {
            return "FLAME LAUNCHER [" + ChatColor.GREEN + "▪" + ChatColor.WHITE + " ▪ ▪ ▪ ▪]";
        } else {
            return "FLAME LAUNCHER [▪ ▪ ▪ ▪ ▪]";
        }
    }

    // 实现Weapon接口的空方法(按需补充)
    @Override
    public double getDamage() { return 0; }
    @Override
    public void setDamage(double dp) {}
    @Override
    public Material getMCMaterial() { return null; }
    @Override
    public void setMCMaterial(Material m) {}
    @Override
    public double getReloadTime() { return 0; }
    @Override
    public void setReloadTime(double reloadTime) {}
    @Override
    public int getAmmoNb() { return 0; }
    @Override
    public void setAmmoNb(int ammo) {}
    @Override
    public void onAmmoHitEntity(AmmoHitPlayerEvent event) {}
}

第二步:创建独立的事件监听器类

package fr.laponie.ShooterPlugin;

import org.bukkit.ChatColor;
import org.bukkit.Location;
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.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.util.Vector;
import net.minecraft.server.v1_8_R3.EnumParticle;
import net.minecraft.server.v1_8_R3.PacketPlayOutWorldParticles;
import org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer;

import java.util.List;

public class WeaponListener implements Listener {
    
    @EventHandler
    public void onRightClickWeapon(PlayerInteractEvent e) {
        Player p = e.getPlayer();
        Action action = e.getAction();
        ItemStack hand = p.getItemInHand();
        
        // 过滤非火焰发射器的物品
        if (hand == null || hand.getType() != Material.GOLD_BARDING) return;
        if (!isFlameLauncher(hand)) return;
        if (action != Action.RIGHT_CLICK_AIR && action != Action.RIGHT_CLICK_BLOCK) return;
        
        // 获取当前物品的燃料值
        double fuel = getFuelFromItem(hand);
        if (fuel <= 0) return;
        
        // 消耗燃料
        fuel -= 1;
        
        // 更新物品的燃料数据和显示名称
        updateItemFuel(hand, fuel);
        
        // 播放粒子效果
        Location playerEyeLoc = p.getEyeLocation();
        Vector director = playerEyeLoc.getDirection();
        EnumParticle particle = EnumParticle.FLAME;
        
        Location ploc = playerEyeLoc.add(director);
        PacketPlayOutWorldParticles packet = new PacketPlayOutWorldParticles(
            particle, false, 
            (float) ploc.getX(), (float) ploc.getY(), (float) ploc.getZ(),
            0, 0, 0, 0, 5, null
        );
        
        CraftPlayer cp = (CraftPlayer) p;
        cp.getHandle().playerConnection.sendPacket(packet);
    }
    
    // 判断物品是否为火焰发射器
    private boolean isFlameLauncher(ItemStack item) {
        if (!item.hasItemMeta()) return false;
        ItemMeta meta = item.getItemMeta();
        return meta.hasDisplayName() && meta.getDisplayName().startsWith("FLAME LAUNCHER");
    }
    
    // 从Lore中读取隐藏的燃料值
    private double getFuelFromItem(ItemStack item) {
        ItemMeta meta = item.getItemMeta();
        List<String> lore = meta.getLore();
        String fuelLine = lore.get(lore.size() - 1);
        String fuelStr = fuelLine.replace(ChatColor.MAGIC + "Fuel:", "");
        return Double.parseDouble(fuelStr);
    }
    
    // 更新物品的燃料数据和显示名称
    private void updateItemFuel(ItemStack item, double newFuel) {
        ItemMeta meta = item.getItemMeta();
        List<String> lore = meta.getLore();
        
        // 更新隐藏的燃料行
        lore.set(lore.size() - 1, ChatColor.MAGIC + "Fuel:" + newFuel);
        meta.setLore(lore);
        
        // 更新显示名称
        meta.setDisplayName(FlameLauncher.getDisplayNameByFuel(newFuel));
        
        item.setItemMeta(meta);
    }
}

第三步:在主类中注册监听器和生成物品

在插件主类的onEnable方法中注册监听器:

getServer().getPluginManager().registerEvents(new WeaponListener(), this);

给玩家发放火焰发射器时调用:

player.getInventory().addItem(FlameLauncher.createFlameLauncher());
关键改动说明
  • 独立物品实例:每次调用createFlameLauncher()都会生成新的ItemStack,每个物品的Lore中存储自己的燃料数据,完全独立。
  • 监听器解耦:不再依赖FlameLauncher实例的成员变量,所有操作都针对当前玩家手中的具体物品。
  • 数据持久化:燃料数据存在物品的Lore中,玩家下线再上线后,物品的燃料值不会丢失。

内容的提问来源于stack exchange,提问作者Manewow

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 00:48:16