diff --git a/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/utilities/PenaltyTime.java b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/utilities/PenaltyTime.java new file mode 100644 index 00000000..2d3c3b2c --- /dev/null +++ b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/utilities/PenaltyTime.java @@ -0,0 +1,67 @@ +package fr.neatmonster.nocheatplus.utilities; + +/** + * Simple penalty duration checker for "current ms", taking into account clocks running backwards. + * @author mc_dev + * + */ +public class PenaltyTime { + + /** Last time a penalty was dealt (for consistency). */ + private long penaltyLast = 0; + + /** Time when the penalty ends. Penalty ends with hitting the penaltyEnd time (equality).*/ + private long penaltyEnd = 0; + + /** + * Merges new penalty time. + * @param now Current ms time. + */ + public void applyPenalty(long duration) { + applyPenalty(System.currentTimeMillis(), duration); + } + + /** + * Merges new penalty time. + * @param now Current ms time. + * @param duration Penalty duration in ms. + */ + public void applyPenalty(long now, long duration) { + penaltyLast = now; + if (now < penaltyLast) { + penaltyEnd = now + duration; + } else { + penaltyEnd = Math.max(now + duration, penaltyEnd); + } + } + + /** + * Test if a penalty applies right now. + * @return + */ + public boolean isPenalty() { + return isPenalty(System.currentTimeMillis()); + } + + /** + * Test if a penalty applies at the given time. Penalty ends with hitting the penaltyEnd time (equality). + * @param now Current time in ms. + * @return + */ + public boolean isPenalty(long now) { + if (now < penaltyLast) { + resetPenalty(); + return false; + } else { + return now < penaltyEnd; + } + } + + /** + * Reset the penalty. + */ + public void resetPenalty() { + penaltyLast = 0; + penaltyEnd = 0; + } +} diff --git a/NCPCommons/src/test/java/fr/neatmonster/nocheatplus/test/TestPenaltyTime.java b/NCPCommons/src/test/java/fr/neatmonster/nocheatplus/test/TestPenaltyTime.java new file mode 100644 index 00000000..1f60c141 --- /dev/null +++ b/NCPCommons/src/test/java/fr/neatmonster/nocheatplus/test/TestPenaltyTime.java @@ -0,0 +1,60 @@ +package fr.neatmonster.nocheatplus.test; + +import static org.junit.Assert.fail; + +import org.junit.Test; + +import fr.neatmonster.nocheatplus.utilities.PenaltyTime; + + +public class TestPenaltyTime { + + @Test + public void testZeroSequence() { + long now = System.currentTimeMillis(); + PenaltyTime pt = new PenaltyTime(); + pt.applyPenalty(now, 0); + if (pt.isPenalty(now )) { + fail("Expect no penalty with duration 0."); + } + } + + @Test + public void testSequence() { + long now = System.currentTimeMillis(); + PenaltyTime pt = new PenaltyTime(); + for (long i = 0; i < 10000; i ++) { + long j = i % 100; + if (j == 0) { + if (pt.isPenalty(now + i)) { + fail("Expect no penalty at i=" + i); + } + pt.applyPenalty(now + i, 50); + } else if (j < 50) { + if (!pt.isPenalty(now + i)) { + fail("Expect penalty at i=" + i); + } + } else { + if (pt.isPenalty(now + i)) { + fail("Expect no penalty at i=" + i); + } + } + + + } + } + + @Test + public void testReset() { + long now = System.currentTimeMillis(); + PenaltyTime pt = new PenaltyTime(); + pt.applyPenalty(now, 73); + if (pt.isPenalty(now - 1)) { + fail("isPenalty should return false on past time."); + } + pt.applyPenalty(now - 1, 73); + if (pt.isPenalty(now + 72)) { + fail("isPenalty should not return edge time after reset."); + } + } +} diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/combined/Combined.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/combined/Combined.java index 392bcc5e..7dfece4b 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/combined/Combined.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/combined/Combined.java @@ -147,12 +147,12 @@ public class Combined { if (total > threshold){ // Add time final float amount = ((total - threshold) / threshold * 1000f); - data.timeFreeze = Math.max(data.timeFreeze, now + (long) Math.min(Math.max(cc.yawRatePenaltyFactor * amount , cc.yawRatePenaltyMin), cc.yawRatePenaltyMax)); + data.timeFreeze.applyPenalty(now, (long) Math.min(Math.max(cc.yawRatePenaltyFactor * amount , cc.yawRatePenaltyMin), cc.yawRatePenaltyMax)); // TODO: balance (100 ... 200 ) ? if (cc.yawRateImprobable && Improbable.check(player, amount / 100f, now, "combined.yawrate")) cancel = true; } - if (now < data.timeFreeze){ + if (data.timeFreeze.isPenalty()){ cancel = true; } return cancel; diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/combined/CombinedData.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/combined/CombinedData.java index d9914fdc..cde70436 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/combined/CombinedData.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/combined/CombinedData.java @@ -9,6 +9,7 @@ import fr.neatmonster.nocheatplus.checks.access.ACheckData; import fr.neatmonster.nocheatplus.checks.access.CheckDataFactory; import fr.neatmonster.nocheatplus.checks.access.ICheckData; import fr.neatmonster.nocheatplus.utilities.ActionFrequency; +import fr.neatmonster.nocheatplus.utilities.PenaltyTime; public class CombinedData extends ACheckData { @@ -65,8 +66,8 @@ public class CombinedData extends ACheckData { public float sumYaw; public final ActionFrequency yawFreq = new ActionFrequency(3, 333); - // General penalty time (used for fighting mainly, set by yawrate check). - public long timeFreeze = 0; + // General penalty time. Used for fighting mainly, but not only close combat (!), set by yawrate check. + public final PenaltyTime timeFreeze = new PenaltyTime(); // Bedleave check public boolean wasInBed = false; diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/combined/CombinedListener.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/combined/CombinedListener.java index 60977367..8aae46a2 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/combined/CombinedListener.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/combined/CombinedListener.java @@ -16,7 +16,10 @@ import fr.neatmonster.nocheatplus.checks.CheckType; import fr.neatmonster.nocheatplus.utilities.TickTask; /** - * GarbageCollectior class to combine some things, make available for other checks, or just because they don't fit into another section. + * Class to combine some things, make available for other checks, or just because they don't fit into another section.
+ * This is registered before the FightListener. + * Do note the registration order in fr.neatmonster.nocheatplus.NoCheatPlus.onEnable (within NCPPlugin). + * * @author mc_dev * */ diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/Direction.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/Direction.java index 75abb4e8..25d1b8a4 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/Direction.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/Direction.java @@ -86,25 +86,14 @@ public class Direction extends Check { // cancel the event. cancel = executeActions(player, data.directionVL, distance, cc.directionActions); - if (cancel) - // If we should cancel, remember the current time too. - data.directionLastViolationTime = System.currentTimeMillis(); + if (cancel) { + // Deal an attack penalty time. + data.attackPenalty.applyPenalty(cc.directionPenalty); + } } else // Reward the player by lowering their violation level. data.directionVL *= 0.8D; - // If the player is still in penalty time, cancel the event anyway. - if (data.directionLastViolationTime + cc.directionPenalty > System.currentTimeMillis()) { - // A safeguard to avoid people getting stuck in penalty time indefinitely in case the system time of the - // server gets changed. - // TODO: Change this to a general attack penalty time. - if (data.directionLastViolationTime > System.currentTimeMillis()) - data.directionLastViolationTime = 0; - - // They are in penalty time, therefore request cancelling of the event. - return true; - } - return cancel; } } 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 2af1a144..19c5dc76 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 @@ -66,6 +66,8 @@ public class FightConfig extends ACheckConfig { public final boolean angleCheck; public final int angleThreshold; public final ActionList angleActions; + + public final long toolChangeAttackPenalty; public final boolean criticalCheck; public final double criticalFallDistance; @@ -132,6 +134,8 @@ public class FightConfig extends ACheckConfig { angleCheck = data.getBoolean(ConfPaths.FIGHT_ANGLE_CHECK); angleThreshold = data.getInt(ConfPaths.FIGHT_ANGLE_THRESHOLD); angleActions = data.getOptimizedActionList(ConfPaths.FIGHT_ANGLE_ACTIONS, Permissions.FIGHT_ANGLE); + + toolChangeAttackPenalty = data.getLong(ConfPaths.FIGHT_TOOLCHANGEPENALTY); criticalCheck = data.getBoolean(ConfPaths.FIGHT_CRITICAL_CHECK); criticalFallDistance = data.getDouble(ConfPaths.FIGHT_CRITICAL_FALLDISTANCE); diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/FightData.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/FightData.java index 5e812508..37c762e2 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/FightData.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/FightData.java @@ -15,6 +15,7 @@ import fr.neatmonster.nocheatplus.checks.access.ICheckData; import fr.neatmonster.nocheatplus.checks.access.SubCheckDataFactory; import fr.neatmonster.nocheatplus.hooks.APIUtils; import fr.neatmonster.nocheatplus.utilities.ActionFrequency; +import fr.neatmonster.nocheatplus.utilities.PenaltyTime; /* * MM""""""""`M oo dP dP M""""""'YMM dP @@ -144,6 +145,9 @@ public class FightData extends ACheckData { public double lastAttackedY; public double lastAttackedZ; + /** Attack penalty (close combat, ENTITY_ATTACK). */ + public final PenaltyTime attackPenalty = new PenaltyTime(); + /** The entity id which might get counter-attacked. */ public int thornsId = Integer.MIN_VALUE; @@ -154,9 +158,6 @@ public class FightData extends ACheckData { // Data of the angle check. public TreeMap angleHits = new TreeMap(); - - // Data of the direction check. - public long directionLastViolationTime = 0; // FastHeal public long fastHealRefTime = 0; @@ -183,7 +184,6 @@ public class FightData extends ACheckData { public boolean noSwingArmSwung; // Data of the reach check. - public long reachLastViolationTime; public double reachMod = 1; // Data of the SelfHit check. 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 8a2180dd..e08bd269 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 @@ -15,6 +15,7 @@ import org.bukkit.event.entity.EntityRegainHealthEvent; import org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason; import org.bukkit.event.player.PlayerAnimationEvent; import org.bukkit.event.player.PlayerChangedWorldEvent; +import org.bukkit.event.player.PlayerItemHeldEvent; import org.bukkit.event.player.PlayerToggleSprintEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; @@ -46,7 +47,8 @@ import fr.neatmonster.nocheatplus.utilities.build.BuildParameters; * d8888P */ /** - * Central location to listen to events that are relevant for the fight checks. + * Central location to listen to events that are relevant for the fight checks.
+ * This listener is registered after the CombinedListener. * * @see FightEvent */ @@ -248,6 +250,15 @@ public class FightListener extends CheckListener implements JoinLeaveListener{ } } + // Generic attacking penalty. + // (Cancel after sprinting hacks, because of potential fp). + if (!cancelled && data.attackPenalty.isPenalty(now)) { + cancelled = true; + if (cc.debug) { + System.out.println(player.getName() + " ~ attack penalty."); + } + } + return cancelled; } @@ -475,4 +486,14 @@ public class FightListener extends CheckListener implements JoinLeaveListener{ final FightData data = FightData.getData(event.getPlayer()); data.angleHits.clear(); } + + @EventHandler(ignoreCancelled = false, priority = EventPriority.MONITOR) + public void onItemHeld(final PlayerItemHeldEvent event) { + final Player player = event.getPlayer(); + final long penalty = FightConfig.getConfig(player).toolChangeAttackPenalty; + if (penalty > 0 ) { + FightData.getData(player).attackPenalty.applyPenalty(penalty); + } + } + } diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/Reach.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/Reach.java index 69cc3240..087f9728 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/Reach.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/fight/Reach.java @@ -112,14 +112,16 @@ public class Reach extends Check { if (Improbable.check(player, (float) violation / 2f, System.currentTimeMillis(), "fight.reach")){ cancel = true; } - if (cancel){ - // If we should cancel, remember the current time too. - data.reachLastViolationTime = System.currentTimeMillis(); + if (cancel && cc.reachPenalty > 0){ + // Apply an attack penalty time. + data.attackPenalty.applyPenalty(cc.reachPenalty); } } else if (lenpRel - distanceLimit * reachMod > 0){ // Silent cancel. - data.reachLastViolationTime = Math.max(data.reachLastViolationTime, System.currentTimeMillis() - cc.reachPenalty / 2); + if (cc.reachPenalty > 0) { + data.attackPenalty.applyPenalty(cc.reachPenalty / 2); + } cancel = true; Improbable.feed(player, (float) (lenpRel - distanceLimit * reachMod) / 4f, System.currentTimeMillis()); } @@ -138,25 +140,9 @@ public class Reach extends Check { else{ data.reachMod = Math.min(1.0, data.reachMod + DYNAMIC_STEP); } - final boolean cancelByPenalty; - // If the player is still in penalty time, cancel the event anyway. - if (data.reachLastViolationTime + cc.reachPenalty > System.currentTimeMillis()) { - // A safeguard to avoid people getting stuck in penalty time indefinitely in case the system time of the - // server gets changed. - if (data.reachLastViolationTime > System.currentTimeMillis()){ - data.reachLastViolationTime = 0; - } - - // They are in penalty time, therefore request cancelling of the event. - cancelByPenalty = !cancel; - cancel = true; - } - else{ - cancelByPenalty = false; - } if (cc.debug && player.hasPermission(Permissions.ADMINISTRATION_DEBUG)){ - player.sendMessage("NC+: Attack " + (cancel ? (cancelByPenalty ? "(cancel/penalty) ":"(cancel/reach) ") : "") + damaged.getType()+ " height="+ StringUtil.fdec3.format(height) + " dist=" + StringUtil.fdec3.format(lenpRel) +" @" + StringUtil.fdec3.format(reachMod)); + player.sendMessage("NC+: Attack/reach " + damaged.getType()+ " height="+ StringUtil.fdec3.format(height) + " dist=" + StringUtil.fdec3.format(lenpRel) +" @" + StringUtil.fdec3.format(reachMod)); } return cancel; 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 7cbf155c..cccff990 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/ConfPaths.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/ConfPaths.java @@ -430,6 +430,7 @@ 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"; private static final String FIGHT_ANGLE = FIGHT + "angle."; public static final String FIGHT_ANGLE_CHECK = FIGHT_ANGLE + "active"; 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 f462397a..22c7610d 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/DefaultConfig.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/config/DefaultConfig.java @@ -318,6 +318,7 @@ public class DefaultConfig extends ConfigFile { */ set(ConfPaths.FIGHT_CANCELDEAD, true); set(ConfPaths.FIGHT_YAWRATE_CHECK, true); + set(ConfPaths.FIGHT_TOOLCHANGEPENALTY, 500L); set(ConfPaths.FIGHT_ANGLE_CHECK, true); set(ConfPaths.FIGHT_ANGLE_THRESHOLD, 50);