Fix NCP in flight exemptions after arrow dismount
This commit is contained in:
1 parent
5a5ab5f029
commit
ce0def7f8d
2 files changed
+85
-4
No files matched your search
@@ -13,8 +13,11 @@ import org.spigotmc.event.entity.EntityDismountEvent;
|
||||
|
||||
// Single cleanup point for the NoCheatPlus MOVING exemption taken in
|
||||
// OnEntityShootBowEvent. EntityDismountEvent fires for every way a ride ends -- the
|
||||
// arrow landing and being removed, the self-hit removal, manual dismount, or death --
|
||||
// so restoring the checks here guarantees the exemption never leaks past the ride.
|
||||
// arrow landing and being removed, the self-hit removal, manual dismount, or death.
|
||||
// We do not restore checks immediately: the player still carries the arrow's momentum
|
||||
// and coasts through the air, which NCP would flag as MOVING_CREATIVEFLY. NCPHook holds
|
||||
// the exemption until the player has landed (bounded), so the exemption never leaks past
|
||||
// the ride yet still covers the post-ride coast.
|
||||
public class OnEntityDismountEvent implements Listener {
|
||||
|
||||
@EventHandler
|
||||
@@ -32,7 +35,10 @@ public class OnEntityDismountEvent implements Listener {
|
||||
if (arrow.getBasePotionData().getType() != PotionType.UNCRAFTABLE)
|
||||
return;
|
||||
|
||||
NCPHook.unexemptMoving(player);
|
||||
// Keep the exemption until the player has bled off the arrow's momentum and
|
||||
// landed -- the post-ride coast is checked as a normal player move and would
|
||||
// otherwise raise MOVING_CREATIVEFLY.
|
||||
NCPHook.unexemptMovingWhenGrounded(player);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -3,10 +3,14 @@
|
||||
package net.trueog.flyingbowog;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.logging.Level;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
|
||||
// Soft integration with NoCheatPlus. While a player rides a Flying Bow arrow they
|
||||
// move through the air faster than survival movement allows. NCP would flag that as
|
||||
@@ -42,6 +46,20 @@ public final class NCPHook {
|
||||
private static boolean warnedNotApplied = false;
|
||||
private static boolean warnedInvokeFailed = false;
|
||||
|
||||
// Pending "unexempt once grounded" pollers, keyed by player. One per player; a
|
||||
// new
|
||||
// ride cancels the previous poller so a late landing can never unexempt
|
||||
// mid-flight.
|
||||
private static final Map<UUID, BukkitTask> pendingUnexempt = new ConcurrentHashMap<>();
|
||||
|
||||
// Cap on how long the exemption is held after dismount while waiting for the
|
||||
// player
|
||||
// to land (server ticks). A fast launch coasts for a few seconds; this bounds
|
||||
// the
|
||||
// worst case so the exemption can never leak permanently if "on ground" never
|
||||
// fires.
|
||||
private static final int MAX_GROUNDING_TICKS = 200;
|
||||
|
||||
private NCPHook() {
|
||||
|
||||
}
|
||||
@@ -52,6 +70,9 @@ public final class NCPHook {
|
||||
if (!resolve())
|
||||
return;
|
||||
|
||||
// A new ride supersedes any pending post-ride grounding poll.
|
||||
cancelPendingUnexempt(player.getUniqueId());
|
||||
|
||||
try {
|
||||
|
||||
exemptMethod.invoke(null, player, movingCheckType);
|
||||
@@ -81,7 +102,52 @@ public final class NCPHook {
|
||||
|
||||
}
|
||||
|
||||
// Restore MOVING checking once the ride ends.
|
||||
// Called when the Flying Bow arrow ride ends (dismount). The player keeps the
|
||||
// arrow's momentum after the arrow is gone and coasts through the air for many
|
||||
// ticks; NCP checks those moves as the player -- not as a vehicle -- and raises
|
||||
// MOVING_CREATIVEFLY. Dropping the exemption the instant the arrow dies leaves
|
||||
// that
|
||||
// coast unprotected. Instead, hold the exemption until the player is back on
|
||||
// the
|
||||
// ground (bounded by MAX_GROUNDING_TICKS), then restore checking.
|
||||
public static void unexemptMovingWhenGrounded(Player player) {
|
||||
|
||||
if (!resolve())
|
||||
return;
|
||||
|
||||
UUID id = player.getUniqueId();
|
||||
cancelPendingUnexempt(id);
|
||||
|
||||
Plugin plugin = FlyingBowOG.getPlugin();
|
||||
// Already grounded, or no scheduler available: restore immediately.
|
||||
if (plugin == null || player.isOnGround()) {
|
||||
|
||||
unexemptMoving(player);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
final int[] ticks = { 0 };
|
||||
BukkitTask task = Bukkit.getScheduler().runTaskTimer(plugin, () -> {
|
||||
|
||||
Player online = Bukkit.getPlayer(id);
|
||||
ticks[0]++;
|
||||
|
||||
if (online == null || !online.isOnline() || online.isOnGround() || ticks[0] >= MAX_GROUNDING_TICKS) {
|
||||
|
||||
cancelPendingUnexempt(id);
|
||||
if (online != null)
|
||||
unexemptMoving(online);
|
||||
|
||||
}
|
||||
|
||||
}, 1L, 1L);
|
||||
|
||||
pendingUnexempt.put(id, task);
|
||||
|
||||
}
|
||||
|
||||
// Restore MOVING checking now. Used internally once the player has landed.
|
||||
public static void unexemptMoving(Player player) {
|
||||
|
||||
if (!resolve())
|
||||
@@ -104,6 +170,15 @@ public final class NCPHook {
|
||||
|
||||
}
|
||||
|
||||
// Cancel and forget any pending grounding poller for a player.
|
||||
private static void cancelPendingUnexempt(UUID id) {
|
||||
|
||||
BukkitTask task = pendingUnexempt.remove(id);
|
||||
if (task != null)
|
||||
task.cancel();
|
||||
|
||||
}
|
||||
|
||||
// Bind the NCP exemption API on first use. Returns whether the hook is usable.
|
||||
private static synchronized boolean resolve() {
|
||||
|
||||
|
||||
Reference in new issue
Block a user