Fix NCP incompatibility

This commit is contained in:
NotAlexNoyle committed 2026-05-27 06:51:24 -04:00
1 parent 6c629e3a5b
commit e3fd3e3f06
3 files changed
+34 -19

No files matched your search

@@ -11,10 +11,10 @@ import org.bukkit.event.Listener;
import org.bukkit.potion.PotionType;
import org.spigotmc.event.entity.EntityDismountEvent;
// Single cleanup point for the NoCheatPlus MOVING_CREATIVEFLY exemption taken in
// 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 check here guarantees the exemption never leaks past the ride.
// so restoring the checks here guarantees the exemption never leaks past the ride.
public class OnEntityDismountEvent implements Listener {
@EventHandler
@@ -32,7 +32,7 @@ public class OnEntityDismountEvent implements Listener {
if (arrow.getBasePotionData().getType() != PotionType.UNCRAFTABLE)
return;
NCPHook.unexemptCreativeFly(player);
NCPHook.unexemptMoving(player);
}
@@ -77,9 +77,10 @@ public class OnEntityShootBowEvent implements Listener {
rideArrow.addPassenger(shooter);
// Stop NoCheatPlus from flagging the arrow ride as illegal flight
// (MOVING_CREATIVEFLY). Cleared on dismount in OnEntityDismountEvent.
NCPHook.exemptCreativeFly(shooter);
// Stop NoCheatPlus from flagging the arrow ride as illegal movement
// (exempts the whole MOVING group: vehicle, survivalfly, nofall, ...).
// Cleared on dismount in OnEntityDismountEvent.
NCPHook.exemptMoving(shooter);
}, 1L);
@@ -7,9 +7,22 @@ import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
// Soft integration with NoCheatPlus. While a player rides a Flying Bow arrow they
// move through the air faster than survival movement allows, and NCP's CreativeFly
// check (MOVING_CREATIVEFLY) flags that as illegal flight, setting the player back or
// kicking them. We exempt that single check for the duration of the ride.
// move through the air faster than survival movement allows. NCP would flag that as
// illegal movement and set the player back or kick them, so we exempt them from the
// whole MOVING check group for the duration of the ride.
//
// We exempt the MOVING group, not a single leaf check, because the arrow ride trips
// more than one moving check and the leaf that fires changes with player state:
// * While the player is a passenger of the arrow, NCP early-returns out of the
// player-move path and runs its VEHICLE checks (MOVING_VEHICLE*) instead.
// * Any player-move not inside the vehicle (launch frame, dismount momentum) is
// handled by SURVIVALFLY, not CREATIVEFLY, since the rider has no creative
// flight. NoFall also applies on the way down.
// NCP exemption is hierarchical only downward -- exempting a parent visits all of
// its descendants, but isExempted() reads a single node without walking up. So
// exempting the MOVING group is the one call that covers every sub-check the ride
// can hit; exempting only MOVING_CREATIVEFLY (as this hook used to) exempted a leaf
// the ride never reaches.
//
// NCP is an optional dependency (softdepend). The exemption API is reached by
// reflection rather than a compile-time dependency: NoCheatPlus publishes a broken
@@ -20,27 +33,26 @@ import org.bukkit.entity.Player;
public final class NCPHook {
// Cached reflection handles, resolved once on first use. ready == true means
// all
// handles bound and exemption calls will be attempted.
// all handles bound and exemption calls will be attempted.
private static boolean resolved = false;
private static boolean ready = false;
private static Method exemptMethod;
private static Method unexemptMethod;
private static Object creativeFlyCheckType;
private static Object movingCheckType;
private NCPHook() {
}
// Exempt the player from MOVING_CREATIVEFLY while riding a Flying Bow arrow.
public static void exemptCreativeFly(Player player) {
// Exempt the player from all MOVING checks while riding a Flying Bow arrow.
public static void exemptMoving(Player player) {
if (!resolve())
return;
try {
exemptMethod.invoke(null, player, creativeFlyCheckType);
exemptMethod.invoke(null, player, movingCheckType);
} catch (Throwable ignored) {
@@ -49,15 +61,15 @@ public final class NCPHook {
}
// Restore MOVING_CREATIVEFLY checking once the ride ends.
public static void unexemptCreativeFly(Player player) {
// Restore MOVING checking once the ride ends.
public static void unexemptMoving(Player player) {
if (!resolve())
return;
try {
unexemptMethod.invoke(null, player, creativeFlyCheckType);
unexemptMethod.invoke(null, player, movingCheckType);
} catch (Throwable ignored) {
@@ -81,7 +93,9 @@ public final class NCPHook {
Class<?> checkType = Class.forName("fr.neatmonster.nocheatplus.checks.CheckType");
Class<?> exemptionManager = Class.forName("fr.neatmonster.nocheatplus.hooks.NCPExemptionManager");
creativeFlyCheckType = Enum.valueOf(checkType.asSubclass(Enum.class), "MOVING_CREATIVEFLY");
// MOVING is the parent group of SURVIVALFLY, CREATIVEFLY, VEHICLE, NOFALL,
// etc. Exempting it cascades to all of them for the duration of the ride.
movingCheckType = Enum.valueOf(checkType.asSubclass(Enum.class), "MOVING");
exemptMethod = exemptionManager.getMethod("exemptPermanently", Player.class, checkType);
unexemptMethod = exemptionManager.getMethod("unexempt", Player.class, checkType);