diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/FightConfig.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/FightConfig.java index a87b66ad..cb640a80 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/FightConfig.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/FightConfig.java @@ -111,6 +111,7 @@ public class FightConfig extends ACheckConfig { // Special flags: public final boolean yawRateCheck; public final boolean cancelDead; + public final boolean knockBackVelocityPvP; /** * Instantiates a new fight configuration. @@ -176,6 +177,7 @@ public class FightConfig extends ACheckConfig { yawRateCheck = data.getBoolean(ConfPaths.FIGHT_YAWRATE_CHECK, true); cancelDead = data.getBoolean(ConfPaths.FIGHT_CANCELDEAD); + knockBackVelocityPvP = data.getBoolean(ConfPaths.FIGHT_PVP_KNOCKBACKVELOCITY); } /* (non-Javadoc) diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/FightListener.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/FightListener.java index 2aebb5c9..a827c7c8 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/FightListener.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/FightListener.java @@ -3,6 +3,7 @@ package fr.neatmonster.nocheatplus.checks.fight; import java.util.Iterator; import org.bukkit.Location; +import org.bukkit.Material; import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; @@ -21,6 +22,7 @@ import org.bukkit.event.player.PlayerItemHeldEvent; import org.bukkit.event.player.PlayerToggleSprintEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; +import org.bukkit.util.Vector; import fr.neatmonster.nocheatplus.NCPAPIProvider; import fr.neatmonster.nocheatplus.checks.CheckListener; @@ -39,6 +41,7 @@ import fr.neatmonster.nocheatplus.components.JoinLeaveListener; import fr.neatmonster.nocheatplus.logging.Streams; import fr.neatmonster.nocheatplus.permissions.Permissions; import fr.neatmonster.nocheatplus.stats.Counters; +import fr.neatmonster.nocheatplus.utilities.BlockProperties; import fr.neatmonster.nocheatplus.utilities.TickTask; import fr.neatmonster.nocheatplus.utilities.TrigUtil; import fr.neatmonster.nocheatplus.utilities.build.BuildParameters; @@ -233,7 +236,7 @@ public class FightListener extends CheckListener implements JoinLeaveListener{ // TODO: Permission ? cancelled = true; } - + if (!cancelled) { final boolean reachEnabled = reach.isEnabled(player); final boolean directionEnabled = direction.isEnabled(player); @@ -253,7 +256,7 @@ public class FightListener extends CheckListener implements JoinLeaveListener{ } } } - + // Check angle with allowed window. if (angle.isEnabled(player)) { // TODO: Revise, use own trace. @@ -321,7 +324,7 @@ public class FightListener extends CheckListener implements JoinLeaveListener{ return cancelled; } - + /** * Quick split-off: Checks using a location trace. * @param player @@ -338,18 +341,18 @@ public class FightListener extends CheckListener implements JoinLeaveListener{ * @return If to cancel (true) or not (false). */ private boolean locationTraceChecks(Player player, Location loc, FightData data, FightConfig cc, Entity damaged, Location damagedLoc, LocationTrace damagedTrace, long tick, boolean reachEnabled, boolean directionEnabled) { - // TODO: Order / splitting off generic stuff. + // TODO: Order / splitting off generic stuff. boolean cancelled = false; - + // (Might pass generic context to factories, for shared + heavy properties.) final SharedContext sharedContext = new SharedContext(damaged, mcAccess); final ReachContext reachContext = reachEnabled ? reach.getContext(player, loc, damaged, damagedLoc, data, cc, sharedContext) : null; final DirectionContext directionContext = directionEnabled ? direction.getContext(player, loc, damaged, damagedLoc, data, cc, sharedContext) : null; - + final long traceOldest = tick; // - damagedTrace.getMaxSize(); // TODO: Set by window. // TODO: Iterating direction, which, static/dynamic choice. final Iterator traceIt = damagedTrace.maxAgeIterator(traceOldest); - + boolean violation = true; // No tick with all checks passed. boolean reachPassed = !reachEnabled; // Passed individually for some tick. boolean directionPassed = !directionEnabled; // Passed individually for some tick. @@ -399,7 +402,7 @@ public class FightListener extends CheckListener implements JoinLeaveListener{ // TODO: Log exact state, probably record min/max latency (individually). return cancelled; } - + /** * Check if a player might return some damage due to the "thorns" enchantment. * @param player @@ -431,10 +434,12 @@ public class FightListener extends CheckListener implements JoinLeaveListener{ final FightData damagedData = damagedPlayer == null ? null : FightData.getData(damagedPlayer); final boolean damagedIsDead = damaged.isDead(); if (damagedPlayer != null && !damagedIsDead) { - if (!damagedPlayer.isDead() && godMode.isEnabled(damagedPlayer) && godMode.check(damagedPlayer, BridgeHealth.getDamage(event), damagedData)){ + // God mode check. + if (godMode.isEnabled(damagedPlayer) && godMode.check(damagedPlayer, BridgeHealth.getDamage(event), damagedData)){ // It requested to "cancel" the players invulnerability, so set their noDamageTicks to 0. damagedPlayer.setNoDamageTicks(0); } + // Adjust buffer for fast heal checks. if (BridgeHealth.getHealth(damagedPlayer) >= BridgeHealth.getMaxHealth(damagedPlayer)){ // TODO: Might use the same FightData instance for GodMode. if (damagedData.fastHealBuffer < 0){ @@ -500,16 +505,65 @@ public class FightListener extends CheckListener implements JoinLeaveListener{ public void onEntityDamageMonitor(final EntityDamageEvent event) { final Entity damaged = event.getEntity(); if (damaged instanceof Player){ - final Player player = (Player) damaged; - final FightData data = FightData.getData(player); - final int ndt = player.getNoDamageTicks(); - if (data.lastDamageTick == TickTask.getTick() && data.lastNoDamageTicks != ndt){ + final Player damagedPlayer = (Player) damaged; + final FightData damagedData = FightData.getData(damagedPlayer); + final int ndt = damagedPlayer.getNoDamageTicks(); + if (damagedData.lastDamageTick == TickTask.getTick() && damagedData.lastNoDamageTicks != ndt){ // Plugin compatibility thing. - data.lastNoDamageTicks = ndt; + damagedData.lastNoDamageTicks = ndt; + } + // Knockback calculation. + switch (event.getCause()) { + case ENTITY_ATTACK: + if (event instanceof EntityDamageByEntityEvent) { + final Entity entity = ((EntityDamageByEntityEvent) event).getDamager(); + if ((entity instanceof Player) && FightConfig.getConfig(damagedPlayer).knockBackVelocityPvP) { + checkKnockBack((Player) entity, damagedPlayer, damagedData); + } + } + default: + break; } } } + /** + * Knockback accounting: Add velocity. + * @param attacker + * @param damagedPlayer + * @param damagedData + */ + private void checkKnockBack(final Player attacker, final Player damagedPlayer, final FightData damagedData) { + double level = 1.0; // Assume "some knock-back" always. + if (attacker.isSprinting()) { + level += 1.0; + } + final ItemStack stack = attacker.getItemInHand(); + if (stack != null && stack.getType() != Material.AIR) { + level += (double) stack.getEnchantmentLevel(Enchantment.KNOCKBACK); + } + if (level > 0.0) { + final MovingData mdata = MovingData.getData(damagedPlayer); + final MovingConfig mcc = MovingConfig.getConfig(damagedPlayer); + // Cap the level to something reasonable. TODO: Config. + level = Math.min(20.0, level); + // TODO: How is the direction really calculated? + final Vector dir = attacker.getLocation(useLoc1).getDirection().multiply(0.5 * level); + + double vy = dir.getY(); + if (BlockProperties.isOnGround(damagedPlayer, damagedPlayer.getLocation(useLoc1), mcc.yOnGround)) { + // (Re-used useLoc1 without need for cleanup.) + vy = 0.365; + } + useLoc1.setWorld(null); // Cleanup. + if (damagedData.debug || mdata.debug) { + NCPAPIProvider.getNoCheatPlusAPI().getLogManager().debug(Streams.TRACE_FILE, damagedPlayer.getName() + " received knockback level: " + level); + } + MovingListener.addVelocity(damagedPlayer, mdata, mcc, dir.getX(), vy, dir.getZ()); + } + + } + /** * We listen to death events to prevent a very specific method of doing godmode. * diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingListener.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingListener.java index dff9f648..8c9c8b2e 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingListener.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingListener.java @@ -1010,57 +1010,64 @@ public class MovingListener extends CheckListener implements TickListener, IRemo * @param event * the event */ - @EventHandler( - ignoreCancelled = true, priority = EventPriority.MONITOR) + @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) public void onPlayerVelocity(final PlayerVelocityEvent event) { final Player player = event.getPlayer(); final MovingData data = MovingData.getData(player); - // Ignore velocity if inside of vehicles. + // Ignore players who are in vehicles. if (player.isInsideVehicle()) { data.removeAllVelocity(); return; } + // Process velocity. + final Vector velocity = event.getVelocity(); final MovingConfig cc = MovingConfig.getConfig(player); + addVelocity(player, data, cc, velocity.getX(), velocity.getY(), velocity.getZ()); + } + + /** + * Add velocity to internal book-keeping. + * @param player + * @param data + * @param cc + * @param vx + * @param vy + * @param vz + */ + public static void addVelocity(final Player player, final MovingData data, final MovingConfig cc, final double vx, final double vy, final double vz) { final int tick = TickTask.getTick(); data.removeInvalidVelocity(tick - cc.velocityActivationTicks); - - final Vector velocity = event.getVelocity(); - if (data.debug) { - NCPAPIProvider.getNoCheatPlusAPI().getLogManager().debug(Streams.TRACE_FILE, event.getPlayer().getName() + " new velocity: " + velocity); + NCPAPIProvider.getNoCheatPlusAPI().getLogManager().debug(Streams.TRACE_FILE, player.getName() + " new velocity: " + vx + ", " + vy + ", " + vz); } - double newVal = velocity.getY(); boolean used = false; - if (newVal >= 0D) { // TODO: Just >, not >=. + if (vy > 0D) { used = true; if (data.verticalFreedom <= 0.001 && data.verticalVelocityCounter >= 0) { data.verticalVelocity = 0; } - data.verticalVelocity += newVal; + data.verticalVelocity += vy; data.verticalFreedom += data.verticalVelocity; - data.verticalVelocityCounter = Math.min(100, Math.max(data.verticalVelocityCounter, cc.velocityGraceTicks ) + 1 + (int) Math.round(newVal * 10.0)); // 50; + data.verticalVelocityCounter = Math.min(100, Math.max(data.verticalVelocityCounter, cc.velocityGraceTicks ) + 1 + (int) Math.round(vy * 10.0)); // 50; data.verticalVelocityUsed = 0; } - newVal = Math.sqrt(velocity.getX() * velocity.getX() + velocity.getZ() * velocity.getZ()); - if (newVal > 0D) { + + if (vx != 0.0 && vz != 0.0) { + final double newVal = Math.sqrt(vx * vx + vz * vz); used = true; final Velocity vel = new Velocity(tick, newVal, cc.velocityActivationCounter, Math.max(20, 1 + (int) Math.round(newVal * 10.0))); data.addHorizontalVelocity(vel); - // data.horizontalFreedom += newVal; - // data.horizontalVelocityCounter = Math.min(100, Math.max(data.horizontalVelocityCounter, cc.velocityGraceTicks ) + 1 + (int) Math.round(newVal * 10.0)); // 30; - // data.horizontalVelocityUsed = 0; } // Set dirty flag here. if (used) { - data.sfDirty = true; + data.sfDirty = true; // TODO: Only needed for vertical velocity? Get rid anyway :p. data.sfNoLowJump = true; } - // TODO: clear accounting here ? } @@ -1281,7 +1288,7 @@ public class MovingListener extends CheckListener implements TickListener, IRemo * @param cc */ private void dataOnJoin(Player player, Location loc, MovingData data, MovingConfig cc) { - + final int tick = TickTask.getTick(); // Check loaded chunks. if (cc.loadChunksOnJoin) { @@ -1291,7 +1298,7 @@ public class MovingListener extends CheckListener implements TickListener, IRemo StaticLog.logInfo("[NoCheatPlus] Player join: Loaded " + loaded + " chunk" + (loaded == 1 ? "" : "s") + " for the world " + loc.getWorld().getName() + " for player: " + player.getName()); } } - + // Correct set-back on join. if (!data.hasSetBack() || data.hasSetBackWorldChanged(loc)) { data.clearFlyData(); @@ -1304,7 +1311,7 @@ public class MovingListener extends CheckListener implements TickListener, IRemo // TODO: If to reset positions: relate to previous ones and set-back. data.resetPositions(loc); // TODO: See above. } - + // Always reset position to this one. // TODO: more fine grained reset? data.clearMorePacketsData(); diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/ConfPaths.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/ConfPaths.java index 9dc625da..2aa289dc 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/ConfPaths.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/ConfPaths.java @@ -376,7 +376,9 @@ public abstract class ConfPaths { public static final String FIGHT = CHECKS + "fight."; public static final String FIGHT_CANCELDEAD = FIGHT + "canceldead"; - public static final String FIGHT_TOOLCHANGEPENALTY = FIGHT + "toolchangepenalty"; + public static final String FIGHT_TOOLCHANGEPENALTY = FIGHT + "toolchangepenalty"; + private static final String FIGHT_PVP = FIGHT + "pvp."; + public static final String FIGHT_PVP_KNOCKBACKVELOCITY = FIGHT_PVP + "knockbackvelocity"; private static final String FIGHT_ANGLE = FIGHT + "angle."; public static final String FIGHT_ANGLE_CHECK = FIGHT_ANGLE + "active"; @@ -645,5 +647,4 @@ public abstract class ConfPaths { @Deprecated public static final String BLOCKBREAK_FASTBREAK_DEBUG = "checks.blockbreak.fastbreak.debug"; - } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/DefaultConfig.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/DefaultConfig.java index 68679dc8..551b9c28 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/DefaultConfig.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/DefaultConfig.java @@ -262,6 +262,7 @@ public class DefaultConfig extends ConfigFile { // FIGHT set(ConfPaths.FIGHT_CANCELDEAD, true); set(ConfPaths.FIGHT_TOOLCHANGEPENALTY, 500L); + set(ConfPaths.FIGHT_PVP_KNOCKBACKVELOCITY, true); set(ConfPaths.FIGHT_YAWRATE_CHECK, true);