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
}
}
}
我最近为我的插件编写了一个 "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
}
}
}