Minecraft(Bukkit):"Doublejump" 后坠落伤害问题

Minecraft (Bukkit) : Issue with falldamage after "Doublejump"

我最近为我的插件编写了一个 "Double Jump" 代码,它也取消了成功跳跃后的坠落伤害。

目前的问题是,falldamage 被完全移除,我找不到原因。问题似乎是 onFall 事件,以及 onMove.

中的 .setAllowFlight(true);
package at.skyblock.events;

import java.util.ArrayList;
import java.util.HashMap;

import org.bukkit.ChatColor;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.entity.EntityDamageEvent.DamageCause;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerToggleFlightEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.util.Vector;

import at.skyblok.SnowflakeUtil;

public class MovementHandler implements Listener {

    private SnowflakeUtil pl;
    private ArrayList<Player> jumpers = new ArrayList<Player>();
    private HashMap<Player, Integer> cooldownTime = new HashMap<Player, Integer>();
    private HashMap<Player, BukkitRunnable> cooldownTask = new HashMap<Player, BukkitRunnable>();

    public MovementHandler(SnowflakeUtil pl) {

        this.pl = pl;

    }

    @EventHandler
    public void onFall(EntityDamageEvent e) {

        if (e.getEntity() instanceof Player) {
            if (e.getCause().equals(DamageCause.FALL)) {
                Player p = (Player) e.getEntity();

                if (jumpers.contains(p)) {

                    e.setCancelled(true);
                    jumpers.remove(p);

                }
            }

        }

    }

    @EventHandler
    public void onMove(final PlayerMoveEvent event) {

        if (cooldownTime.containsKey(event.getPlayer()))
            return;

        if (event.getPlayer().getGameMode() != GameMode.CREATIVE
                && event.getPlayer().getLocation().getBlock().getRelative(BlockFace.DOWN).getType() != Material.AIR) {

            event.getPlayer().sendMessage("ready");

            event.getPlayer().setAllowFlight(true);

            cooldownTime.put(event.getPlayer(), 5);

            cooldownTask.put(event.getPlayer(), new BukkitRunnable() {
                public void run() {
                    cooldownTime.put(event.getPlayer(), cooldownTime.get(event.getPlayer()) - 1);

                    if (cooldownTime.get(event.getPlayer()) == 0) {

                        cooldownTime.remove(event.getPlayer());
                        cooldownTask.remove(event.getPlayer());
                        jumpers.remove(event.getPlayer());
                        cancel();
                    }
                }
            });

            cooldownTask.get(event.getPlayer()).runTaskTimer(pl, 20, 20);

        }

    }

    @EventHandler(priority = EventPriority.HIGH)
    public void onDoubleJump(PlayerToggleFlightEvent e) {

        Player p = e.getPlayer();
        if (!p.getGameMode().equals(GameMode.CREATIVE)) {
            e.setCancelled(true);
            p.setFlying(false);
            p.setAllowFlight(false);

            String type = "";

            if (p.getInventory().getArmorContents() != null) {
                for (ItemStack is : p.getInventory().getArmorContents()) {

                    if (is.hasItemMeta()) {
                        if (is.getItemMeta().hasLore()) {
                            for (int i = 0; i < is.getItemMeta().getLore().size(); i++) {

                                if (ChatColor.stripColor(is.getItemMeta().getLore().get(i).toLowerCase())
                                        .contains(ChatColor.stripColor("movement"))) {

                                    String part = ChatColor.stripColor(is.getItemMeta().getLore().get(i));

                                    type = SnowflakeUtil
                                            .capitalize(part.replaceAll("Movement:", "").replaceAll("\s", ""));

                                }
                            }
                        }

                    }
                }

            }
            jumpers.add(p);
            switch (type) {
            case "Rocketjump":

                p.setVelocity(p.getLocation().getDirection().multiply(1));
                p.setVelocity(new Vector(p.getVelocity().getX(), 0.75D, p.getVelocity().getZ()));

                break;

            }

        }
    }

}

你有没有 return 使用它后它默认为 ex: return false;函数?

包括玩家跳跃的权利。

@EventHandler(priority = EventPriority.HIGH)
public void onFallDamage(EntityDamageEvent event){
    if(event.getEntity() instanceof Player){
        Player player = (Player)event.getEntity();
        if(event.getCause()){
            event.setCancelled(true);
        }
    }
}

这里的障碍是 player.setAllowFlight(true) 让玩家免疫掉落伤害,但不幸的是,这是由客户端而不是服务器完成的。客户端根本不会让服务器知道它们掉落了,所以不会触发 EntityDamageEvent 并且我们无法取消取消服务器可能已经阻止的任何损坏(服务器不会检查是否一个玩家摔倒了,而是依靠客户端在他们摔倒时告诉服务器)。

然而,由于玩家需要能够在整个坠落过程中切换飞行模式,以便触发 PlayerFlightToggleEvent(如果他们还没有使用二段跳),我们需要一种方法来检测就在玩家即将着陆之前的那一刻,如果玩家还没有使用他们的双跳能力,我们可以安全地假设他们已经决定根本不使用它并且正在下降 "regularly" 和因此应该承受坠落伤害。如果我们在此之前禁用玩家使用二段跳的能力,我们将创建一个(不必要的)权衡,玩家可能无法在着陆前的一瞬间(甚至更早)进行二段跳。

通过检查玩家在 PlayerMoveEvent 期间即将移动到的位置正下方的方块,我们可以 "predict" 玩家是否正在着陆在非空中堵塞。在 PlayerMoveEvent 实际发生之前,我们禁用飞行模式,以便玩家在移动完成后定期受到坠落伤害。代码看起来像这样:

// Inside your "PlayerMoveEvent" method

// If a player is not on the ground and has fallen more than two blocks
if (!((CraftPlayer) player).isOnGround() && player.getFallDistance() > 2) {
    Location to = event.getTo().clone().subtract(0, 0.0001, 0); // Get the location they will be at next tick
    if (to.getBlock().getType() != Material.AIR) { // If that block is not air
        player.setAllowFlight(false); // Cancel their ability to fly so that they take regular fall damage
    }
}

请注意,玩家实体的坠落距离和 onGround 值由客户端提供,可以用自定义客户端进行欺骗,因此使用他们脚下的方块来检查它们是否是否在地面上(尽管由于其他各种原因,这有时也不是 return 正确的结果,球员是否真的在地面上比看起来更复杂)。

代码的另一个提示:如果你想为某些能力创建某种冷却时间,而不是创建一个相对耗费资源的任务来倒计时,你可以只放置玩家和时间当他们可以在地图内再次使用该能力时。如果他们再次尝试使用该能力,您可以检查当前时间是否超过允许他们再次使用该能力的时间点。示例:

private HashMap<UUID, Long> abilityCooldown = new HashMap<>();

public void onEvent(SomePlayerEvent event) {
    // Player is trying to use some ability...

    // It's usually safer to store IDs rather than names or player objects for various reasons
    UUID id = event.getPlayer().getUniqueId();
    if (!abilityCooldown.containsKey(id)) { // They are not in the map, so have never tried to use the ability yet
        // Let them use the ability here...
        int seconds = 5; // The amount of time to cool down for
        // Put the player's ID and the time when they will be allowed to use the ability again (future)
        abilityCooldown.put(id, System.currentTimeMillis() + 1000 * seconds);
    } else {
        // The time when they are allowed to use the ability again (we put this in the map when they used it last)
        long time = abilityCooldown.get(id);
        if (time > System.currentTimeMillis()) { // If that time is still in the future
            // Do not allow them to use the ability (maybe send them a message)
        } else {
            // Let them use the ability here...
            int seconds = 5; // The amount of time to cool down for
            abilityCooldown.put(id, System.currentTimeMillis() + 1000 * seconds); // Update the time when they can use the ablity again
        }
    }
}