Bukkit枪械插件递归方法无限调用致StackOverflowError求助
Bukkit枪械插件弹药逻辑问题解决
问题分析
- 递归栈溢出:原
Ammo.moveFrom()方法仅在命中沙子时终止递归,未命中则无限调用自身,直接触发StackOverflowError。 - 循环版本失效:修改后的循环逻辑向量计算错误——每次都重新获取玩家当前视角方向并叠加固定向量,导致弹药无法沿发射方向直线前进,位置混乱;同时缺少射程限制,可能陷入死循环。
核心修正点
- 改用Bukkit异步调度+循环实现弹药飞行(避免卡住服务器主线程)
- 修正向量计算:基于发射瞬间的初始方向,按固定步长递增位置
- 添加最大射程限制,防止无限循环
- 扩展碰撞检测:命中所有非空气方块时执行摧毁逻辑
修正后的代码
重构后的Ammo类
package fr.laponie.ShooterPlugin; import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.util.Vector; import net.minecraft.server.v1_8_R3.EnumParticle; import net.minecraft.server.v1_8_R3.PacketPlayOutWorldParticles; public class Ammo { private final Player p; private final Weapon w; private Location currentLoc; private final Vector direction; private final double step = 0.4; // 每次移动步长 private final int maxRange = 50; // 最大射程(方块数) private int traveledTicks = 0; public Ammo(Player p, Weapon w) { this.p = p; this.w = w; this.currentLoc = p.getEyeLocation().clone(); // 初始化发射方向:标准化后乘以步长,移除错误的(1,1,1)叠加 this.direction = currentLoc.getDirection().normalize().multiply(step); } // 启动弹药飞行逻辑 public void launch() { new BukkitRunnable() { @Override public void run() { traveledTicks++; // 更新当前位置 currentLoc.add(direction); Block targetBlock = currentLoc.getBlock(); // 发送飞行粒子特效 sendParticle(currentLoc); // 终止条件:超出射程或命中非空气方块 if (traveledTicks >= maxRange || targetBlock.getType() != Material.AIR) { if (targetBlock.getType() != Material.AIR) { p.sendMessage(ChatColor.DARK_AQUA + "" + ChatColor.BOLD + "命中方块:" + targetBlock.getType().name()); targetBlock.setType(Material.AIR); } this.cancel(); return; } } }.runTaskTimer(w.getPlugin(), 0, 1); // 每1tick执行一次(20tick=1秒) } // 封装粒子发送逻辑 private void sendParticle(Location loc) { EnumParticle trail = EnumParticle.CRIT; PacketPlayOutWorldParticles packParticle = new PacketPlayOutWorldParticles( trail, false, (float) loc.getX(), (float) loc.getY(), (float) loc.getZ(), 0, 0, 0, 0, 0, null ); ((CraftPlayer) p).getHandle().playerConnection.sendPacket(packParticle); } }
修正后的PlayerInteractEvent处理
@EventHandler public void onRightClickWeapon(PlayerInteractEvent e) { Player p = e.getPlayer(); Action a = e.getAction(); ItemStack current = p.getItemInHand(); ItemStack rifle = new AssaultRifle().mat; if ((a == Action.RIGHT_CLICK_AIR || a == Action.RIGHT_CLICK_BLOCK) && current != null) { // 匹配枪械类型与名称 if (current.getType() == rifle.getType() && current.hasItemMeta() && current.getItemMeta().getDisplayName().equalsIgnoreCase(rifle.getItemMeta().getDisplayName())) { Ammo ammo = new Ammo(p, this); ammo.launch(); // 启动弹药飞行逻辑 } } }
关键改进说明
- 异步调度:用
BukkitRunnable实现非阻塞循环,避免拖慢服务器 - 直线飞行逻辑:基于发射瞬间的方向向量,保证弹药沿直线前进
- 安全限制:通过
maxRange防止无限循环,同时避免弹药飞出地图边界 - 通用碰撞处理:命中所有非空气方块都触发摧毁,满足需求
- 代码解耦:将粒子发送逻辑抽为独立方法,提升代码可维护性
内容的提问来源于stack exchange,提问作者Manewow
相关产品推荐
相关产品推荐

