mirror of
https://github.com/NoCheatPlus/NoCheatPlus.git
synced 2024-10-06 10:27:26 +02:00
Spaces + comments, modifier.
This commit is contained in:
parent
8b55139003
commit
24ccc4a5ea
@ -32,12 +32,12 @@ public class Direction extends Check {
|
||||
* @return true, if successful
|
||||
*/
|
||||
public boolean check(final Player player, final Location loc, final Entity damaged, final Location dLoc, final FightData data, final FightConfig cc) {
|
||||
boolean cancel = false;
|
||||
boolean cancel = false;
|
||||
|
||||
// Safeguard, if entity is complex, this check will fail due to giant and hard to define hitboxes.
|
||||
// if (damaged instanceof EntityComplex || damaged instanceof EntityComplexPart)
|
||||
// if (damaged instanceof EntityComplex || damaged instanceof EntityComplexPart)
|
||||
if (mcAccess.isComplexPart(damaged)) {
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find out how wide the entity is.
|
||||
@ -45,20 +45,20 @@ public class Direction extends Check {
|
||||
|
||||
// entity.height is broken and will always be 0, therefore. Calculate height instead based on boundingBox.
|
||||
final double height = mcAccess.getHeight(damaged);
|
||||
|
||||
|
||||
// TODO: allow any hit on the y axis (might just adapt interface to use foot position + height)!
|
||||
|
||||
|
||||
// How far "off" is the player with their aim. We calculate from the players eye location and view direction to
|
||||
// the center of the target entity. If the line of sight is more too far off, "off" will be bigger than 0.
|
||||
final Vector direction = loc.getDirection();
|
||||
|
||||
|
||||
final double off;
|
||||
if (cc.directionStrict){
|
||||
off = TrigUtil.combinedDirectionCheck(loc, player.getEyeHeight(), direction, dLoc.getX(), dLoc.getY() + height / 2D, dLoc.getZ(), width, height, TrigUtil.DIRECTION_PRECISION, 80.0);
|
||||
off = TrigUtil.combinedDirectionCheck(loc, player.getEyeHeight(), direction, dLoc.getX(), dLoc.getY() + height / 2D, dLoc.getZ(), width, height, TrigUtil.DIRECTION_PRECISION, 80.0);
|
||||
}
|
||||
else{
|
||||
// Also take into account the angle.
|
||||
off = TrigUtil.directionCheck(loc, player.getEyeHeight(), direction, dLoc.getX(), dLoc.getY() + height / 2D, dLoc.getZ(), width, height, TrigUtil.DIRECTION_PRECISION);
|
||||
// Also take into account the angle.
|
||||
off = TrigUtil.directionCheck(loc, player.getEyeHeight(), direction, dLoc.getX(), dLoc.getY() + height / 2D, dLoc.getZ(), width, height, TrigUtil.DIRECTION_PRECISION);
|
||||
}
|
||||
|
||||
if (off > 0.1) {
|
||||
@ -74,17 +74,17 @@ public class Direction extends Check {
|
||||
cancel = executeActions(player, data.directionVL, distance, cc.directionActions);
|
||||
|
||||
if (cancel) {
|
||||
// Deal an attack penalty time.
|
||||
data.attackPenalty.applyPenalty(cc.directionPenalty);
|
||||
// Deal an attack penalty time.
|
||||
data.attackPenalty.applyPenalty(cc.directionPenalty);
|
||||
}
|
||||
} else {
|
||||
// Reward the player by lowering their violation level.
|
||||
// Reward the player by lowering their violation level.
|
||||
data.directionVL *= 0.8D;
|
||||
}
|
||||
|
||||
|
||||
return cancel;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Data context for iterating over TraceEntry instances.
|
||||
* @param player
|
||||
@ -95,50 +95,50 @@ public class Direction extends Check {
|
||||
* @param cc
|
||||
* @return
|
||||
*/
|
||||
public DirectionContext getContext(final Player player, final Location loc, final Entity damaged, final Location damagedLoc, final FightData data, final FightConfig cc) {
|
||||
final DirectionContext context = new DirectionContext();
|
||||
context.damagedComplex = mcAccess.isComplexPart(damaged);
|
||||
// Find out how wide the entity is.
|
||||
public DirectionContext getContext(final Player player, final Location loc, final Entity damaged, final Location damagedLoc, final FightData data, final FightConfig cc) {
|
||||
final DirectionContext context = new DirectionContext();
|
||||
context.damagedComplex = mcAccess.isComplexPart(damaged);
|
||||
// Find out how wide the entity is.
|
||||
context.damagedWidth = mcAccess.getWidth(damaged);
|
||||
// entity.height is broken and will always be 0, therefore. Calculate height instead based on boundingBox.
|
||||
context.damagedHeight = mcAccess.getHeight(damaged);
|
||||
context.direction = loc.getDirection();
|
||||
context.lengthDirection = context.direction.length();
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the player fails the direction check, no change of FightData.
|
||||
* @param player
|
||||
* @param loc
|
||||
* @param damaged
|
||||
* @param dLoc
|
||||
* @param context
|
||||
* @param data
|
||||
* @param cc
|
||||
* @return
|
||||
*/
|
||||
public boolean loopCheck(final Player player, final Location loc, final Entity damaged, final TraceEntry dLoc, final DirectionContext context, final FightData data, final FightConfig cc) {
|
||||
|
||||
// Ignore complex entities for the moment.
|
||||
if (context.damagedComplex) {
|
||||
// TODO: Revise :p
|
||||
return false;
|
||||
}
|
||||
boolean cancel = false;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the player fails the direction check, no change of FightData.
|
||||
* @param player
|
||||
* @param loc
|
||||
* @param damaged
|
||||
* @param dLoc
|
||||
* @param context
|
||||
* @param data
|
||||
* @param cc
|
||||
* @return
|
||||
*/
|
||||
public boolean loopCheck(final Player player, final Location loc, final Entity damaged, final TraceEntry dLoc, final DirectionContext context, final FightData data, final FightConfig cc) {
|
||||
|
||||
// Ignore complex entities for the moment.
|
||||
if (context.damagedComplex) {
|
||||
// TODO: Revise :p
|
||||
return false;
|
||||
}
|
||||
boolean cancel = false;
|
||||
|
||||
// TODO: allow any hit on the y axis (might just adapt interface to use foot position + height)!
|
||||
|
||||
|
||||
// How far "off" is the player with their aim. We calculate from the players eye location and view direction to
|
||||
// the center of the target entity. If the line of sight is more too far off, "off" will be bigger than 0.
|
||||
|
||||
|
||||
final double off;
|
||||
if (cc.directionStrict){
|
||||
off = TrigUtil.combinedDirectionCheck(loc, player.getEyeHeight(), context.direction, dLoc.x, dLoc.y + context.damagedHeight / 2D, dLoc.z, context.damagedWidth, context.damagedHeight, TrigUtil.DIRECTION_PRECISION, 80.0);
|
||||
off = TrigUtil.combinedDirectionCheck(loc, player.getEyeHeight(), context.direction, dLoc.x, dLoc.y + context.damagedHeight / 2D, dLoc.z, context.damagedWidth, context.damagedHeight, TrigUtil.DIRECTION_PRECISION, 80.0);
|
||||
}
|
||||
else{
|
||||
// Also take into account the angle.
|
||||
off = TrigUtil.directionCheck(loc, player.getEyeHeight(), context.direction, dLoc.x, dLoc.y + context.damagedHeight / 2D, dLoc.z, context.damagedWidth, context.damagedHeight, TrigUtil.DIRECTION_PRECISION);
|
||||
// Also take into account the angle.
|
||||
off = TrigUtil.directionCheck(loc, player.getEyeHeight(), context.direction, dLoc.x, dLoc.y + context.damagedHeight / 2D, dLoc.z, context.damagedWidth, context.damagedHeight, TrigUtil.DIRECTION_PRECISION);
|
||||
}
|
||||
|
||||
if (off > 0.1) {
|
||||
@ -148,28 +148,28 @@ public class Direction extends Check {
|
||||
context.minViolation = Math.min(context.minViolation, distance);
|
||||
}
|
||||
context.minResult = Math.min(context.minResult, off);
|
||||
|
||||
|
||||
return cancel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply changes to FightData according to check results (context), trigger violations.
|
||||
* @param player
|
||||
* @param loc
|
||||
* @param damaged
|
||||
* @param context
|
||||
* @param forceViolation
|
||||
* @param data
|
||||
* @param cc
|
||||
* @return
|
||||
*/
|
||||
public boolean loopFinish(final Player player, final Location loc, final Entity damaged, final DirectionContext context, final boolean forceViolation, final FightData data, final FightConfig cc) {
|
||||
boolean cancel = false;
|
||||
final double off = forceViolation && context.minViolation != Double.MAX_VALUE ? context.minViolation : context.minResult;
|
||||
if (off == Double.MAX_VALUE) {
|
||||
return false;
|
||||
}
|
||||
else if (off > 0.1) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply changes to FightData according to check results (context), trigger violations.
|
||||
* @param player
|
||||
* @param loc
|
||||
* @param damaged
|
||||
* @param context
|
||||
* @param forceViolation
|
||||
* @param data
|
||||
* @param cc
|
||||
* @return
|
||||
*/
|
||||
public boolean loopFinish(final Player player, final Location loc, final Entity damaged, final DirectionContext context, final boolean forceViolation, final FightData data, final FightConfig cc) {
|
||||
boolean cancel = false;
|
||||
final double off = forceViolation && context.minViolation != Double.MAX_VALUE ? context.minViolation : context.minResult;
|
||||
if (off == Double.MAX_VALUE) {
|
||||
return false;
|
||||
}
|
||||
else if (off > 0.1) {
|
||||
// Add the overall violation level of the check.
|
||||
data.directionVL += context.minViolation;
|
||||
|
||||
@ -178,16 +178,16 @@ public class Direction extends Check {
|
||||
cancel = executeActions(player, data.directionVL, context.minViolation, cc.directionActions);
|
||||
|
||||
if (cancel) {
|
||||
// Deal an attack penalty time.
|
||||
data.attackPenalty.applyPenalty(cc.directionPenalty);
|
||||
// Deal an attack penalty time.
|
||||
data.attackPenalty.applyPenalty(cc.directionPenalty);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Reward the player by lowering their violation level.
|
||||
else {
|
||||
// Reward the player by lowering their violation level.
|
||||
data.directionVL *= 0.8D;
|
||||
}
|
||||
|
||||
return cancel;
|
||||
}
|
||||
|
||||
|
||||
return cancel;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -8,16 +8,16 @@ import org.bukkit.util.Vector;
|
||||
*
|
||||
*/
|
||||
public class DirectionContext {
|
||||
|
||||
public boolean damagedComplex;
|
||||
public double damagedWidth;
|
||||
public double damagedHeight;
|
||||
public Vector direction = null;
|
||||
public double lengthDirection;
|
||||
|
||||
/** Minimum value for the distance that was a violation. */
|
||||
public double minViolation = Double.MAX_VALUE;
|
||||
/** Minimum value for off. */
|
||||
public double minResult = Double.MAX_VALUE;
|
||||
|
||||
|
||||
public boolean damagedComplex;
|
||||
public double damagedWidth;
|
||||
public double damagedHeight;
|
||||
public Vector direction = null;
|
||||
public double lengthDirection;
|
||||
|
||||
/** Minimum value for the distance that was a violation. */
|
||||
public double minViolation = Double.MAX_VALUE;
|
||||
/** Minimum value for off. */
|
||||
public double minResult = Double.MAX_VALUE;
|
||||
|
||||
}
|
||||
|
@ -21,13 +21,13 @@ import fr.neatmonster.nocheatplus.permissions.Permissions;
|
||||
*/
|
||||
public class FightConfig extends ACheckConfig {
|
||||
|
||||
/** The factory creating configurations. */
|
||||
public static final CheckConfigFactory factory = new CheckConfigFactory() {
|
||||
@Override
|
||||
public final ICheckConfig getConfig(final Player player) {
|
||||
return FightConfig.getConfig(player);
|
||||
}
|
||||
};
|
||||
/** The factory creating configurations. */
|
||||
public static final CheckConfigFactory factory = new CheckConfigFactory() {
|
||||
@Override
|
||||
public final ICheckConfig getConfig(final Player player) {
|
||||
return FightConfig.getConfig(player);
|
||||
}
|
||||
};
|
||||
|
||||
/** The map containing the configurations per world. */
|
||||
private static final Map<String, FightConfig> worldsMap = new HashMap<String, FightConfig>();
|
||||
@ -56,7 +56,7 @@ 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;
|
||||
@ -65,18 +65,18 @@ public class FightConfig extends ACheckConfig {
|
||||
public final ActionList criticalActions;
|
||||
|
||||
public final boolean directionCheck;
|
||||
public final boolean directionStrict;
|
||||
public final boolean directionStrict;
|
||||
public final long directionPenalty;
|
||||
public final ActionList directionActions;
|
||||
|
||||
|
||||
public final boolean fastHealCheck;
|
||||
public final long fastHealInterval;
|
||||
public final long fastHealBuffer;
|
||||
public final ActionList fastHealActions;
|
||||
|
||||
public final boolean godModeCheck;
|
||||
public final long godModeLagMinAge;
|
||||
public final long godModeLagMaxAge;
|
||||
public final long godModeLagMinAge;
|
||||
public final long godModeLagMaxAge;
|
||||
public final ActionList godModeActions;
|
||||
|
||||
public final boolean knockbackCheck;
|
||||
@ -89,30 +89,30 @@ public class FightConfig extends ACheckConfig {
|
||||
public final boolean reachCheck;
|
||||
public final long reachPenalty;
|
||||
public final boolean reachPrecision;
|
||||
public final boolean reachReduce;
|
||||
public final double reachSurvivalDistance;
|
||||
public final double reachReduceDistance;
|
||||
public final double reachReduceStep;
|
||||
public final boolean reachReduce;
|
||||
public final double reachSurvivalDistance;
|
||||
public final double reachReduceDistance;
|
||||
public final double reachReduceStep;
|
||||
|
||||
public final ActionList reachActions;
|
||||
|
||||
|
||||
public final boolean selfHitCheck;
|
||||
public final ActionList selfHitActions;
|
||||
public final ActionList selfHitActions;
|
||||
|
||||
public final boolean speedCheck;
|
||||
public final int speedLimit;
|
||||
public final int speedBuckets;
|
||||
public final long speedBucketDur;
|
||||
public final float speedBucketFactor;
|
||||
public final int speedBuckets;
|
||||
public final long speedBucketDur;
|
||||
public final float speedBucketFactor;
|
||||
|
||||
public final int speedShortTermLimit;
|
||||
public final int speedShortTermTicks;
|
||||
public final int speedShortTermTicks;
|
||||
public final ActionList speedActions;
|
||||
|
||||
|
||||
// Special flags:
|
||||
public final boolean yawRateCheck;
|
||||
public final boolean cancelDead;
|
||||
|
||||
public final boolean yawRateCheck;
|
||||
public final boolean cancelDead;
|
||||
|
||||
/**
|
||||
* Instantiates a new fight configuration.
|
||||
*
|
||||
@ -124,7 +124,7 @@ 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);
|
||||
@ -136,7 +136,7 @@ public class FightConfig extends ACheckConfig {
|
||||
directionStrict = data.getBoolean(ConfPaths.FIGHT_DIRECTION_STRICT);
|
||||
directionPenalty = data.getLong(ConfPaths.FIGHT_DIRECTION_PENALTY);
|
||||
directionActions = data.getOptimizedActionList(ConfPaths.FIGHT_DIRECTION_ACTIONS, Permissions.FIGHT_DIRECTION);
|
||||
|
||||
|
||||
fastHealCheck = data.getBoolean(ConfPaths.FIGHT_FASTHEAL_CHECK);;
|
||||
fastHealInterval = data.getLong(ConfPaths.FIGHT_FASTHEAL_INTERVAL);
|
||||
fastHealBuffer = data.getLong(ConfPaths.FIGHT_FASTHEAL_BUFFER);
|
||||
@ -165,7 +165,7 @@ public class FightConfig extends ACheckConfig {
|
||||
|
||||
selfHitCheck = data.getBoolean(ConfPaths.FIGHT_SELFHIT_CHECK);
|
||||
selfHitActions = data.getOptimizedActionList(ConfPaths.FIGHT_SELFHIT_ACTIONS, Permissions.FIGHT_SELFHIT);
|
||||
|
||||
|
||||
speedCheck = data.getBoolean(ConfPaths.FIGHT_SPEED_CHECK);
|
||||
speedLimit = data.getInt(ConfPaths.FIGHT_SPEED_LIMIT);
|
||||
speedBuckets = data.getInt(ConfPaths.FIGHT_SPEED_BUCKETS_N, 6);
|
||||
@ -174,8 +174,8 @@ public class FightConfig extends ACheckConfig {
|
||||
speedShortTermLimit = data.getInt(ConfPaths.FIGHT_SPEED_SHORTTERM_LIMIT);
|
||||
speedShortTermTicks = data.getInt(ConfPaths.FIGHT_SPEED_SHORTTERM_TICKS);
|
||||
speedActions = data.getOptimizedActionList(ConfPaths.FIGHT_SPEED_ACTIONS, Permissions.FIGHT_SPEED);
|
||||
|
||||
|
||||
|
||||
|
||||
yawRateCheck = data.getBoolean(ConfPaths.FIGHT_YAWRATE_CHECK, true);
|
||||
cancelDead = data.getBoolean(ConfPaths.FIGHT_CANCELDEAD);
|
||||
}
|
||||
@ -203,9 +203,9 @@ public class FightConfig extends ACheckConfig {
|
||||
case FIGHT_SPEED:
|
||||
return speedCheck;
|
||||
case FIGHT_SELFHIT:
|
||||
return selfHitCheck;
|
||||
return selfHitCheck;
|
||||
case FIGHT_FASTHEAL:
|
||||
return fastHealCheck;
|
||||
return fastHealCheck;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
|
@ -21,77 +21,77 @@ import fr.neatmonster.nocheatplus.utilities.PenaltyTime;
|
||||
* Player specific data for the fight checks.
|
||||
*/
|
||||
public class FightData extends ACheckData {
|
||||
|
||||
public static class FightDataFactory implements CheckDataFactory {
|
||||
|
||||
protected FightDataFactory() {
|
||||
// Discourage creation here.
|
||||
};
|
||||
|
||||
@Override
|
||||
public final ICheckData getData(final Player player) {
|
||||
return FightData.getData(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICheckData removeData(final String playerName) {
|
||||
return FightData.removeData(playerName);
|
||||
}
|
||||
public static class FightDataFactory implements CheckDataFactory {
|
||||
|
||||
@Override
|
||||
public void removeAllData() {
|
||||
clear();
|
||||
}
|
||||
}
|
||||
protected FightDataFactory() {
|
||||
// Discourage creation here.
|
||||
};
|
||||
|
||||
@Override
|
||||
public final ICheckData getData(final Player player) {
|
||||
return FightData.getData(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICheckData removeData(final String playerName) {
|
||||
return FightData.removeData(playerName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllData() {
|
||||
clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** The factory for general fight data. */
|
||||
public static final CheckDataFactory factory = new FightDataFactory();
|
||||
|
||||
/** SelfHit factory */
|
||||
public static final CheckDataFactory selfHitDataFactory = new SubCheckDataFactory<FightData>(CheckType.FIGHT, factory) {
|
||||
/** The factory for general fight data. */
|
||||
public static final CheckDataFactory factory = new FightDataFactory();
|
||||
|
||||
@Override
|
||||
protected FightData getData(String playerName) {
|
||||
return playersMap.get(playerName);
|
||||
}
|
||||
/** SelfHit factory */
|
||||
public static final CheckDataFactory selfHitDataFactory = new SubCheckDataFactory<FightData>(CheckType.FIGHT, factory) {
|
||||
|
||||
@Override
|
||||
protected Collection<String> getPresentData() {
|
||||
return playersMap.keySet();
|
||||
}
|
||||
@Override
|
||||
protected FightData getData(String playerName) {
|
||||
return playersMap.get(playerName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean hasData(String playerName) {
|
||||
return playersMap.containsKey(playerName);
|
||||
}
|
||||
@Override
|
||||
protected Collection<String> getPresentData() {
|
||||
return playersMap.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean removeFromData(String playerName, FightData data) {
|
||||
if (data.selfHitVL.score(1f) > 0f) {
|
||||
data.selfHitVL.clear(System.currentTimeMillis());
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
public static CheckDataFactory getCheckDataFactory(CheckType checkType) {
|
||||
if (checkType != CheckType.FIGHT && !APIUtils.isParent(CheckType.FIGHT, checkType)) {
|
||||
throw new IllegalArgumentException("Can only return a CheckDataFactory for the check group FIGHT.");
|
||||
}
|
||||
switch(checkType) {
|
||||
// Note that CheckType does need adaption for new entries (!).
|
||||
case FIGHT_SELFHIT:
|
||||
return selfHitDataFactory;
|
||||
default:
|
||||
return factory;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
protected boolean hasData(String playerName) {
|
||||
return playersMap.containsKey(playerName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean removeFromData(String playerName, FightData data) {
|
||||
if (data.selfHitVL.score(1f) > 0f) {
|
||||
data.selfHitVL.clear(System.currentTimeMillis());
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
public static CheckDataFactory getCheckDataFactory(CheckType checkType) {
|
||||
if (checkType != CheckType.FIGHT && !APIUtils.isParent(CheckType.FIGHT, checkType)) {
|
||||
throw new IllegalArgumentException("Can only return a CheckDataFactory for the check group FIGHT.");
|
||||
}
|
||||
switch(checkType) {
|
||||
// Note that CheckType does need adaption for new entries (!).
|
||||
case FIGHT_SELFHIT:
|
||||
return selfHitDataFactory;
|
||||
default:
|
||||
return factory;
|
||||
}
|
||||
}
|
||||
|
||||
/** The map containing the data per players. */
|
||||
protected static final Map<String, FightData> playersMap = new HashMap<String, FightData>(); // Not sure about visibility (selfhit).
|
||||
@ -110,14 +110,14 @@ public class FightData extends ACheckData {
|
||||
}
|
||||
|
||||
public static ICheckData removeData(final String playerName) {
|
||||
return playersMap.remove(playerName);
|
||||
}
|
||||
|
||||
public static void clear(){
|
||||
playersMap.clear();
|
||||
return playersMap.remove(playerName);
|
||||
}
|
||||
|
||||
// Violation levels.
|
||||
public static void clear(){
|
||||
playersMap.clear();
|
||||
}
|
||||
|
||||
// Violation levels.
|
||||
public double angleVL;
|
||||
public double criticalVL;
|
||||
public double directionVL;
|
||||
@ -127,28 +127,28 @@ public class FightData extends ACheckData {
|
||||
public double noSwingVL;
|
||||
public double reachVL;
|
||||
public double speedVL;
|
||||
|
||||
|
||||
// Shared
|
||||
public String lastWorld = "";
|
||||
public int lastAttackTick = 0;
|
||||
public double lastAttackedX = Double.MAX_VALUE;
|
||||
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;
|
||||
|
||||
|
||||
/** Any kind of health regeneration. */
|
||||
public long regainHealthTime = 0;
|
||||
// public double lastAttackedDist = 0.0;
|
||||
public long regainHealthTime = 0;
|
||||
// public double lastAttackedDist = 0.0;
|
||||
public long damageTakenByEntityTick;
|
||||
|
||||
// Data of the angle check.
|
||||
public TreeMap<Long, Location> angleHits = new TreeMap<Long, Location>();
|
||||
|
||||
|
||||
// FastHeal
|
||||
public long fastHealRefTime = 0;
|
||||
/** Buffer has to be initialized in constructor. */
|
||||
@ -158,14 +158,14 @@ public class FightData extends ACheckData {
|
||||
public int godModeBuffer;
|
||||
public int godModeLastAge;
|
||||
public long godModeLastTime;
|
||||
|
||||
|
||||
// New god mode check [in progress].
|
||||
public int godModeHealthDecreaseTick = 0;
|
||||
public double godModeHealth = 0.0;
|
||||
public int godModeHealthDecreaseTick = 0;
|
||||
public double godModeHealth = 0.0;
|
||||
public int lastDamageTick = 0;
|
||||
public int lastNoDamageTicks = 0;
|
||||
/** Accumulator. */
|
||||
public int godModeAcc = 0;
|
||||
public int godModeAcc = 0;
|
||||
|
||||
// Data of the knockback check.
|
||||
public long knockbackSprintTime;
|
||||
@ -174,35 +174,35 @@ public class FightData extends ACheckData {
|
||||
public boolean noSwingArmSwung;
|
||||
|
||||
// Data of the reach check.
|
||||
public double reachMod = 1;
|
||||
|
||||
public double reachMod = 1;
|
||||
|
||||
// Data of the SelfHit check.
|
||||
public ActionFrequency selfHitVL = new ActionFrequency(6, 5000);
|
||||
|
||||
// Data of the frequency check.
|
||||
public final ActionFrequency speedBuckets;
|
||||
public int speedShortTermCount;
|
||||
public int speedShortTermTick;
|
||||
|
||||
// TNT workaround: Allow ENTITY_ATTACK if these attributes match.
|
||||
// Discussion at: https://github.com/NoCheatPlus/NoCheatPlus/pull/17 (@Iceee)
|
||||
/** Tick the last explosion damage was dealt at. */
|
||||
public int lastExplosionDamageTick = -1 ;
|
||||
/** Last explosion damaged entity (id). */
|
||||
public int lastExplosionEntityId = Integer.MAX_VALUE;
|
||||
|
||||
|
||||
public FightData(final FightConfig cc){
|
||||
speedBuckets = new ActionFrequency(cc.speedBuckets, cc.speedBucketDur);
|
||||
// Start with full fast-heal buffer.
|
||||
fastHealBuffer = cc.fastHealBuffer;
|
||||
}
|
||||
public int speedShortTermCount;
|
||||
public int speedShortTermTick;
|
||||
|
||||
// TNT workaround: Allow ENTITY_ATTACK if these attributes match.
|
||||
// Discussion at: https://github.com/NoCheatPlus/NoCheatPlus/pull/17 (@Iceee)
|
||||
/** Tick the last explosion damage was dealt at. */
|
||||
public int lastExplosionDamageTick = -1 ;
|
||||
/** Last explosion damaged entity (id). */
|
||||
public int lastExplosionEntityId = Integer.MAX_VALUE;
|
||||
|
||||
|
||||
public FightData(final FightConfig cc){
|
||||
speedBuckets = new ActionFrequency(cc.speedBuckets, cc.speedBucketDur);
|
||||
// Start with full fast-heal buffer.
|
||||
fastHealBuffer = cc.fastHealBuffer;
|
||||
}
|
||||
|
||||
public void onWorldChange() {
|
||||
angleHits.clear();
|
||||
lastAttackedX = Double.MAX_VALUE;
|
||||
lastAttackTick = 0;
|
||||
lastWorld = "";
|
||||
}
|
||||
|
||||
public void onWorldChange() {
|
||||
angleHits.clear();
|
||||
lastAttackedX = Double.MAX_VALUE;
|
||||
lastAttackTick = 0;
|
||||
lastWorld = "";
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ public class FightListener extends CheckListener implements JoinLeaveListener{
|
||||
|
||||
/** The direction check. */
|
||||
private final Direction direction = addCheck(new Direction());
|
||||
|
||||
|
||||
/** Faster health regeneration check. */
|
||||
private final FastHeal fastHeal = addCheck(new FastHeal());
|
||||
|
||||
@ -74,24 +74,24 @@ public class FightListener extends CheckListener implements JoinLeaveListener{
|
||||
|
||||
/** The reach check. */
|
||||
private final Reach reach = addCheck(new Reach());
|
||||
|
||||
|
||||
/** The self hit check */
|
||||
private final SelfHit selfHit = addCheck(new SelfHit());
|
||||
|
||||
/** The speed check. */
|
||||
private final Speed speed = addCheck(new Speed());
|
||||
|
||||
|
||||
/** For temporary use: LocUtil.clone before passing deeply, call setWorld(null) after use. */
|
||||
private final Location useLoc1 = new Location(null, 0, 0, 0);
|
||||
|
||||
/** For temporary use: LocUtil.clone before passing deeply, call setWorld(null) after use. */
|
||||
private final Location useLoc2 = new Location(null, 0, 0, 0);
|
||||
|
||||
private final Counters counters = NCPAPIProvider.getNoCheatPlusAPI().getGenericInstance(Counters.class);
|
||||
private final Location useLoc1 = new Location(null, 0, 0, 0);
|
||||
|
||||
/** For temporary use: LocUtil.clone before passing deeply, call setWorld(null) after use. */
|
||||
private final Location useLoc2 = new Location(null, 0, 0, 0);
|
||||
|
||||
private final Counters counters = NCPAPIProvider.getNoCheatPlusAPI().getGenericInstance(Counters.class);
|
||||
private final int idCancelDead = counters.registerKey("canceldead");
|
||||
|
||||
|
||||
public FightListener(){
|
||||
super(CheckType.FIGHT);
|
||||
super(CheckType.FIGHT);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -103,27 +103,27 @@ public class FightListener extends CheckListener implements JoinLeaveListener{
|
||||
*/
|
||||
private boolean handleNormalDamage(final Player player, final Entity damaged, final double damage, final int tick, final FightData data) {
|
||||
final FightConfig cc = FightConfig.getConfig(player);
|
||||
|
||||
|
||||
// Hotfix attempt for enchanted books.
|
||||
// TODO: maybe a generaluzed version for the future...
|
||||
final ItemStack stack = player.getItemInHand();
|
||||
// Illegal enchantments hotfix check.
|
||||
if (Items.checkIllegalEnchantments(player, stack)) return true;
|
||||
|
||||
|
||||
boolean cancelled = false;
|
||||
|
||||
|
||||
final String worldName = player.getWorld().getName();
|
||||
final long now = System.currentTimeMillis();
|
||||
final boolean worldChanged = !worldName.equals(data.lastWorld);
|
||||
|
||||
|
||||
final Location loc = player.getLocation(useLoc1);
|
||||
// // Bad pitch/yaw, just in case.
|
||||
// if (LocUtil.needsDirectionCorrection(useLoc1.getYaw(), useLoc1.getPitch())) {
|
||||
// mcAccess.correctDirection(player);
|
||||
// player.getLocation(useLoc1);
|
||||
// }
|
||||
// // Bad pitch/yaw, just in case.
|
||||
// if (LocUtil.needsDirectionCorrection(useLoc1.getYaw(), useLoc1.getPitch())) {
|
||||
// mcAccess.correctDirection(player);
|
||||
// player.getLocation(useLoc1);
|
||||
// }
|
||||
final Location damagedLoc = damaged.getLocation(useLoc2);
|
||||
// final double targetDist = CheckUtils.distance(loc, targetLoc); // TODO: Calculate distance as is done in fight.reach !
|
||||
// final double targetDist = CheckUtils.distance(loc, targetLoc); // TODO: Calculate distance as is done in fight.reach !
|
||||
final double targetMove;
|
||||
final int tickAge;
|
||||
final long msAge; // Milliseconds the ticks actually took.
|
||||
@ -131,267 +131,267 @@ public class FightListener extends CheckListener implements JoinLeaveListener{
|
||||
// TODO: relative distance (player - target)!
|
||||
// TODO: Use trace for this ?
|
||||
if (data.lastAttackedX == Double.MAX_VALUE || tick < data.lastAttackTick || worldChanged || tick - data.lastAttackTick > 20){
|
||||
// TODO: 20 ?
|
||||
tickAge = 0;
|
||||
targetMove = 0.0;
|
||||
normalizedMove = 0.0;
|
||||
msAge = 0;
|
||||
// TODO: 20 ?
|
||||
tickAge = 0;
|
||||
targetMove = 0.0;
|
||||
normalizedMove = 0.0;
|
||||
msAge = 0;
|
||||
}
|
||||
else{
|
||||
tickAge = tick - data.lastAttackTick;
|
||||
// TODO: Maybe use 3d distance if dy(normalized) is too big.
|
||||
targetMove = TrigUtil.distance(data.lastAttackedX, data.lastAttackedZ, damagedLoc.getX(), damagedLoc.getZ());
|
||||
msAge = (long) (50f * TickTask.getLag(50L * tickAge, true) * (float) tickAge);
|
||||
normalizedMove = msAge == 0 ? targetMove : targetMove * Math.min(20.0, 1000.0 / (double) msAge);
|
||||
tickAge = tick - data.lastAttackTick;
|
||||
// TODO: Maybe use 3d distance if dy(normalized) is too big.
|
||||
targetMove = TrigUtil.distance(data.lastAttackedX, data.lastAttackedZ, damagedLoc.getX(), damagedLoc.getZ());
|
||||
msAge = (long) (50f * TickTask.getLag(50L * tickAge, true) * (float) tickAge);
|
||||
normalizedMove = msAge == 0 ? targetMove : targetMove * Math.min(20.0, 1000.0 / (double) msAge);
|
||||
}
|
||||
// TODO: calculate factor for dists: ticks * 50 * lag
|
||||
|
||||
|
||||
// TODO: dist < width => skip some checks (direction, ..)
|
||||
|
||||
|
||||
final LocationTrace damagedTrace;
|
||||
final Player damagedPlayer;
|
||||
if (damaged instanceof Player){
|
||||
damagedPlayer = (Player) damaged;
|
||||
// // Bad pitch/yaw, just in case.
|
||||
// if (LocUtil.needsDirectionCorrection(useLoc2.getYaw(), useLoc2.getPitch())) {
|
||||
// mcAccess.correctDirection(damagedPlayer);
|
||||
// damagedPlayer.getLocation(useLoc2);
|
||||
// }
|
||||
// Log.
|
||||
if (cc.debug && damagedPlayer.hasPermission(Permissions.ADMINISTRATION_DEBUG)){
|
||||
damagedPlayer.sendMessage("Attacked by " + player.getName() + ": inv=" + mcAccess.getInvulnerableTicks(damagedPlayer) + " ndt=" + damagedPlayer.getNoDamageTicks());
|
||||
}
|
||||
// Check for self hit exploits (mind that projectiles are excluded from this.)
|
||||
if (selfHit.isEnabled(player) && selfHit.check(player, damagedPlayer, data, cc)) {
|
||||
cancelled = true;
|
||||
}
|
||||
// Get+update the damaged players.
|
||||
// TODO: Problem with NPCs: data stays (not a big problem).
|
||||
// (This is done even if the event has already been cancelled, to keep track, if the player is on a horse.)
|
||||
damagedTrace = MovingData.getData(damagedPlayer).updateTrace(damagedPlayer, damagedLoc, tick);
|
||||
damagedPlayer = (Player) damaged;
|
||||
// // Bad pitch/yaw, just in case.
|
||||
// if (LocUtil.needsDirectionCorrection(useLoc2.getYaw(), useLoc2.getPitch())) {
|
||||
// mcAccess.correctDirection(damagedPlayer);
|
||||
// damagedPlayer.getLocation(useLoc2);
|
||||
// }
|
||||
// Log.
|
||||
if (cc.debug && damagedPlayer.hasPermission(Permissions.ADMINISTRATION_DEBUG)){
|
||||
damagedPlayer.sendMessage("Attacked by " + player.getName() + ": inv=" + mcAccess.getInvulnerableTicks(damagedPlayer) + " ndt=" + damagedPlayer.getNoDamageTicks());
|
||||
}
|
||||
// Check for self hit exploits (mind that projectiles are excluded from this.)
|
||||
if (selfHit.isEnabled(player) && selfHit.check(player, damagedPlayer, data, cc)) {
|
||||
cancelled = true;
|
||||
}
|
||||
// Get+update the damaged players.
|
||||
// TODO: Problem with NPCs: data stays (not a big problem).
|
||||
// (This is done even if the event has already been cancelled, to keep track, if the player is on a horse.)
|
||||
damagedTrace = MovingData.getData(damagedPlayer).updateTrace(damagedPlayer, damagedLoc, tick);
|
||||
} else {
|
||||
damagedPlayer = null; // TODO: This is a temporary workaround.
|
||||
// Use a fake trace.
|
||||
// TODO: Provide for entities too? E.g. one per player, or a fully fledged bookkeeping thing (EntityData).
|
||||
//final MovingConfig mcc = MovingConfig.getConfig(damagedLoc.getWorld().getName());
|
||||
damagedTrace = null; //new LocationTrace(mcc.traceSize, mcc.traceMergeDist);
|
||||
//damagedTrace.addEntry(tick, damagedLoc.getX(), damagedLoc.getY(), damagedLoc.getZ());
|
||||
damagedPlayer = null; // TODO: This is a temporary workaround.
|
||||
// Use a fake trace.
|
||||
// TODO: Provide for entities too? E.g. one per player, or a fully fledged bookkeeping thing (EntityData).
|
||||
//final MovingConfig mcc = MovingConfig.getConfig(damagedLoc.getWorld().getName());
|
||||
damagedTrace = null; //new LocationTrace(mcc.traceSize, mcc.traceMergeDist);
|
||||
//damagedTrace.addEntry(tick, damagedLoc.getX(), damagedLoc.getY(), damagedLoc.getZ());
|
||||
}
|
||||
|
||||
|
||||
if (cc.cancelDead){
|
||||
if (damaged.isDead()) {
|
||||
cancelled = true;
|
||||
}
|
||||
// Only allow damaging others if taken damage this tick.
|
||||
if (damaged.isDead()) {
|
||||
cancelled = true;
|
||||
}
|
||||
// Only allow damaging others if taken damage this tick.
|
||||
if (player.isDead() && data.damageTakenByEntityTick != TickTask.getTick()){
|
||||
cancelled = true;
|
||||
cancelled = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (damage <= 4.0 && tick == data.damageTakenByEntityTick && data.thornsId != Integer.MIN_VALUE && data.thornsId == damaged.getEntityId()){
|
||||
// Don't handle further, but do respect selfhit/canceldead.
|
||||
// TODO: Remove soon.
|
||||
data.thornsId = Integer.MIN_VALUE;
|
||||
return cancelled;
|
||||
// Don't handle further, but do respect selfhit/canceldead.
|
||||
// TODO: Remove soon.
|
||||
data.thornsId = Integer.MIN_VALUE;
|
||||
return cancelled;
|
||||
}
|
||||
else {
|
||||
data.thornsId = Integer.MIN_VALUE;
|
||||
data.thornsId = Integer.MIN_VALUE;
|
||||
}
|
||||
|
||||
// Run through the main checks.
|
||||
if (!cancelled && speed.isEnabled(player)){
|
||||
if (speed.check(player, now)){
|
||||
cancelled = true;
|
||||
// Still feed the improbable.
|
||||
if (data.speedVL > 50){
|
||||
Improbable.check(player, 2f, now, "fight.speed");
|
||||
}
|
||||
else{
|
||||
Improbable.feed(player, 2f, now);
|
||||
}
|
||||
}
|
||||
else if (normalizedMove > 2.0 && Improbable.check(player, 1f, now, "fight.speed")){
|
||||
// Feed improbable in case of ok-moves too.
|
||||
// TODO: consider only feeding if attacking with higher average speed (!)
|
||||
cancelled = true;
|
||||
}
|
||||
if (speed.check(player, now)){
|
||||
cancelled = true;
|
||||
// Still feed the improbable.
|
||||
if (data.speedVL > 50){
|
||||
Improbable.check(player, 2f, now, "fight.speed");
|
||||
}
|
||||
else{
|
||||
Improbable.feed(player, 2f, now);
|
||||
}
|
||||
}
|
||||
else if (normalizedMove > 2.0 && Improbable.check(player, 1f, now, "fight.speed")){
|
||||
// Feed improbable in case of ok-moves too.
|
||||
// TODO: consider only feeding if attacking with higher average speed (!)
|
||||
cancelled = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!cancelled && critical.isEnabled(player) && critical.check(player, loc, data, cc)) {
|
||||
cancelled = true;
|
||||
cancelled = true;
|
||||
}
|
||||
|
||||
|
||||
if (!cancelled && knockback.isEnabled(player) && knockback.check(player, data, cc)) {
|
||||
cancelled = true;
|
||||
cancelled = true;
|
||||
}
|
||||
|
||||
|
||||
if (!cancelled && noSwing.isEnabled(player) && noSwing.check(player, data, cc)) {
|
||||
cancelled = true;
|
||||
cancelled = true;
|
||||
}
|
||||
|
||||
|
||||
if (!cancelled && player.isBlocking() && !player.hasPermission(Permissions.MOVING_SURVIVALFLY_BLOCKING)) {
|
||||
cancelled = true;
|
||||
cancelled = true;
|
||||
}
|
||||
|
||||
|
||||
// TODO: Order of all these checks ...
|
||||
// Checks that use LocationTrace.
|
||||
|
||||
// TODO: Later optimize (...), should reverse check window ?
|
||||
|
||||
|
||||
// First loop through reach and direction, to determine a window.
|
||||
final boolean reachEnabled = !cancelled && reach.isEnabled(player);
|
||||
final boolean directionEnabled = !cancelled && direction.isEnabled(player);
|
||||
|
||||
|
||||
if (reachEnabled || directionEnabled) {
|
||||
if (damagedPlayer != null) {
|
||||
// TODO: Move to a method (trigonometric checks).
|
||||
if (damagedPlayer != null) {
|
||||
// TODO: Move to a method (trigonometric checks).
|
||||
final ReachContext reachContext = reachEnabled ? reach.getContext(player, loc, damaged, damagedLoc, data, cc) : null;
|
||||
final DirectionContext directionContext = directionEnabled ? direction.getContext(player, loc, damaged, damagedLoc, data, cc) : null;
|
||||
|
||||
|
||||
final long traceOldest = tick; // - damagedTrace.getMaxSize(); // TODO: Set by window.
|
||||
// TODO: Iterating direction: could also start from latest, be it on occasion.
|
||||
Iterator<TraceEntry> traceIt = damagedTrace.maxAgeIterator(traceOldest);
|
||||
|
||||
final Iterator<TraceEntry> 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.
|
||||
// TODO: Maintain a latency estimate + max diff and invalidate completely (i.e. iterate from latest NEXT time)], or just max latency.
|
||||
while (traceIt.hasNext()) {
|
||||
final TraceEntry entry = traceIt.next();
|
||||
// Simplistic just check both until end or hit.
|
||||
// TODO: Other default distances/tolerances.
|
||||
boolean thisPassed = true;
|
||||
if (reachEnabled) {
|
||||
if (reach.loopCheck(player, loc, damagedPlayer, entry, reachContext, data, cc)) {
|
||||
thisPassed = false;
|
||||
} else {
|
||||
reachPassed = true;
|
||||
}
|
||||
}
|
||||
// TODO: For efficiency one could omit checking at all if reach is failed all the time.
|
||||
if (directionEnabled && (reachPassed || !directionPassed)) {
|
||||
if (direction.loopCheck(player, damagedLoc, damagedPlayer, entry, directionContext, data, cc)) {
|
||||
thisPassed = false;
|
||||
} else {
|
||||
directionPassed = true;
|
||||
}
|
||||
}
|
||||
if (thisPassed) {
|
||||
// TODO: Log/set estimated latency.
|
||||
violation = false;
|
||||
break;
|
||||
}
|
||||
final TraceEntry entry = traceIt.next();
|
||||
// Simplistic just check both until end or hit.
|
||||
// TODO: Other default distances/tolerances.
|
||||
boolean thisPassed = true;
|
||||
if (reachEnabled) {
|
||||
if (reach.loopCheck(player, loc, damagedPlayer, entry, reachContext, data, cc)) {
|
||||
thisPassed = false;
|
||||
} else {
|
||||
reachPassed = true;
|
||||
}
|
||||
}
|
||||
// TODO: For efficiency one could omit checking at all if reach is failed all the time.
|
||||
if (directionEnabled && (reachPassed || !directionPassed)) {
|
||||
if (direction.loopCheck(player, damagedLoc, damagedPlayer, entry, directionContext, data, cc)) {
|
||||
thisPassed = false;
|
||||
} else {
|
||||
directionPassed = true;
|
||||
}
|
||||
}
|
||||
if (thisPassed) {
|
||||
// TODO: Log/set estimated latency.
|
||||
violation = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// TODO: How to treat mixed state: violation && reachPassed && directionPassed [current: use min violation // thinkable: silent cancel, if actions have cancel (!)]
|
||||
// TODO: Adapt according to strictness settings?
|
||||
if (reachEnabled) {
|
||||
// TODO: Might ignore if already cancelled by mixed/silent cancel.
|
||||
if (reach.loopFinish(player, loc, damagedPlayer, reachContext, violation, data, cc)) {
|
||||
cancelled = true;
|
||||
}
|
||||
// TODO: Might ignore if already cancelled by mixed/silent cancel.
|
||||
if (reach.loopFinish(player, loc, damagedPlayer, reachContext, violation, data, cc)) {
|
||||
cancelled = true;
|
||||
}
|
||||
}
|
||||
if (directionEnabled) {
|
||||
// TODO: Might ignore if already cancelled.
|
||||
if (direction.loopFinish(player, loc, damagedPlayer, directionContext, violation, data, cc)) {
|
||||
cancelled = true;
|
||||
}
|
||||
// TODO: Might ignore if already cancelled.
|
||||
if (direction.loopFinish(player, loc, damagedPlayer, directionContext, violation, data, cc)) {
|
||||
cancelled = true;
|
||||
}
|
||||
}
|
||||
// TODO: Log exact state, probably record min/max latency (individually).
|
||||
} else {
|
||||
// Still use the classic methods for non-players. maybe[]
|
||||
if (reachEnabled && reach.check(player, loc, damaged, damagedLoc, data, cc)) {
|
||||
cancelled = true;
|
||||
} else {
|
||||
// Still use the classic methods for non-players. maybe[]
|
||||
if (reachEnabled && reach.check(player, loc, damaged, damagedLoc, data, cc)) {
|
||||
cancelled = true;
|
||||
}
|
||||
|
||||
|
||||
if (directionEnabled && direction.check(player, loc, damaged, damagedLoc, data, cc)) {
|
||||
cancelled = true;
|
||||
cancelled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Check angle with allowed window.
|
||||
if (angle.isEnabled(player)) {
|
||||
// TODO: Revise, use own trace.
|
||||
// The "fast turning" checks are checked in any case because they accumulate data.
|
||||
// Improbable yaw changing: Moving events might be missing up to a ten degrees change.
|
||||
if (Combined.checkYawRate(player, loc.getYaw(), now, worldName, cc.yawRateCheck)) {
|
||||
// (Check or just feed).
|
||||
// TODO: Work into this somehow attacking the same aim and/or similar aim position (not cancel then).
|
||||
cancelled = true;
|
||||
}
|
||||
// Angle check.
|
||||
if (angle.check(player, worldChanged, data, cc)) {
|
||||
if (!cancelled && cc.debug) {
|
||||
NCPAPIProvider.getNoCheatPlusAPI().getLogManager().debug(Streams.TRACE_FILE, player.getName() + " fight.angle cancel without yawrate cancel.");
|
||||
}
|
||||
cancelled = true;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Revise, use own trace.
|
||||
// The "fast turning" checks are checked in any case because they accumulate data.
|
||||
// Improbable yaw changing: Moving events might be missing up to a ten degrees change.
|
||||
if (Combined.checkYawRate(player, loc.getYaw(), now, worldName, cc.yawRateCheck)) {
|
||||
// (Check or just feed).
|
||||
// TODO: Work into this somehow attacking the same aim and/or similar aim position (not cancel then).
|
||||
cancelled = true;
|
||||
}
|
||||
// Angle check.
|
||||
if (angle.check(player, worldChanged, data, cc)) {
|
||||
if (!cancelled && cc.debug) {
|
||||
NCPAPIProvider.getNoCheatPlusAPI().getLogManager().debug(Streams.TRACE_FILE, player.getName() + " fight.angle cancel without yawrate cancel.");
|
||||
}
|
||||
cancelled = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Set values.
|
||||
data.lastWorld = worldName;
|
||||
data.lastAttackTick = tick;
|
||||
data.lastAttackedX = damagedLoc.getX();
|
||||
data.lastAttackedY = damagedLoc.getY();
|
||||
data.lastAttackedZ = damagedLoc.getZ();
|
||||
// data.lastAttackedDist = targetDist;
|
||||
|
||||
// Care for the "lost sprint problem": sprint resets, client moves as if still...
|
||||
// TODO: Use stored distance calculation same as reach check?
|
||||
// TODO: For pvp: make use of "player was there" heuristic later on.
|
||||
// TODO: Confine further with simple pre-conditions.
|
||||
// TODO: Evaluate if moving traces can help here.
|
||||
if (!cancelled && TrigUtil.distance(loc.getX(), loc.getZ(), damagedLoc.getX(), damagedLoc.getZ()) < 4.5){
|
||||
final MovingData mData = MovingData.getData(player);
|
||||
// Check if fly checks is an issue at all, re-check "real sprinting".
|
||||
if (mData.fromX != Double.MAX_VALUE && mData.mediumLiftOff != MediumLiftOff.LIMIT_JUMP){
|
||||
final double hDist = TrigUtil.distance(loc.getX(), loc.getZ(), mData.fromX, mData.fromZ);
|
||||
if (hDist >= 0.23) {
|
||||
// TODO: Might need to check hDist relative to speed / modifiers.
|
||||
final MovingConfig mc = MovingConfig.getConfig(player);
|
||||
if (now <= mData.timeSprinting + mc.sprintingGrace && MovingListener.shouldCheckSurvivalFly(player, mData, mc)){
|
||||
// Judge as "lost sprint" problem.
|
||||
// TODO: What would mData.lostSprintCount > 0 mean here?
|
||||
mData.lostSprintCount = 7;
|
||||
if ((cc.debug || mc.debug) && BuildParameters.debugLevel > 0){
|
||||
NCPAPIProvider.getNoCheatPlusAPI().getLogManager().debug(Streams.TRACE_FILE, player.getName() + " (lostsprint) hDist to last from: " + hDist + " | targetdist=" + TrigUtil.distance(loc.getX(), loc.getZ(), damagedLoc.getX(), damagedLoc.getZ()) + " | sprinting=" + player.isSprinting() + " | food=" + player.getFoodLevel() +" | hbuf=" + mData.sfHorizontalBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generic attacking penalty.
|
||||
// (Cancel after sprinting hacks, because of potential fp).
|
||||
if (!cancelled && data.attackPenalty.isPenalty(now)) {
|
||||
cancelled = true;
|
||||
if (cc.debug) {
|
||||
NCPAPIProvider.getNoCheatPlusAPI().getLogManager().debug(Streams.TRACE_FILE, player.getName() + " ~ attack penalty.");
|
||||
}
|
||||
data.lastAttackTick = tick;
|
||||
data.lastAttackedX = damagedLoc.getX();
|
||||
data.lastAttackedY = damagedLoc.getY();
|
||||
data.lastAttackedZ = damagedLoc.getZ();
|
||||
// data.lastAttackedDist = targetDist;
|
||||
|
||||
// Care for the "lost sprint problem": sprint resets, client moves as if still...
|
||||
// TODO: Use stored distance calculation same as reach check?
|
||||
// TODO: For pvp: make use of "player was there" heuristic later on.
|
||||
// TODO: Confine further with simple pre-conditions.
|
||||
// TODO: Evaluate if moving traces can help here.
|
||||
if (!cancelled && TrigUtil.distance(loc.getX(), loc.getZ(), damagedLoc.getX(), damagedLoc.getZ()) < 4.5){
|
||||
final MovingData mData = MovingData.getData(player);
|
||||
// Check if fly checks is an issue at all, re-check "real sprinting".
|
||||
if (mData.fromX != Double.MAX_VALUE && mData.mediumLiftOff != MediumLiftOff.LIMIT_JUMP){
|
||||
final double hDist = TrigUtil.distance(loc.getX(), loc.getZ(), mData.fromX, mData.fromZ);
|
||||
if (hDist >= 0.23) {
|
||||
// TODO: Might need to check hDist relative to speed / modifiers.
|
||||
final MovingConfig mc = MovingConfig.getConfig(player);
|
||||
if (now <= mData.timeSprinting + mc.sprintingGrace && MovingListener.shouldCheckSurvivalFly(player, mData, mc)){
|
||||
// Judge as "lost sprint" problem.
|
||||
// TODO: What would mData.lostSprintCount > 0 mean here?
|
||||
mData.lostSprintCount = 7;
|
||||
if ((cc.debug || mc.debug) && BuildParameters.debugLevel > 0){
|
||||
NCPAPIProvider.getNoCheatPlusAPI().getLogManager().debug(Streams.TRACE_FILE, player.getName() + " (lostsprint) hDist to last from: " + hDist + " | targetdist=" + TrigUtil.distance(loc.getX(), loc.getZ(), damagedLoc.getX(), damagedLoc.getZ()) + " | sprinting=" + player.isSprinting() + " | food=" + player.getFoodLevel() +" | hbuf=" + mData.sfHorizontalBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup.
|
||||
|
||||
// Generic attacking penalty.
|
||||
// (Cancel after sprinting hacks, because of potential fp).
|
||||
if (!cancelled && data.attackPenalty.isPenalty(now)) {
|
||||
cancelled = true;
|
||||
if (cc.debug) {
|
||||
NCPAPIProvider.getNoCheatPlusAPI().getLogManager().debug(Streams.TRACE_FILE, player.getName() + " ~ attack penalty.");
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup.
|
||||
useLoc1.setWorld(null);
|
||||
useLoc2.setWorld(null);
|
||||
|
||||
|
||||
return cancelled;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if a player might return some damage due to the "thorns" enchantment.
|
||||
* @param player
|
||||
* @return
|
||||
*/
|
||||
public static final boolean hasThorns(final Player player){
|
||||
final PlayerInventory inv = player.getInventory();
|
||||
final ItemStack[] contents = inv.getArmorContents();
|
||||
for (int i = 0; i < contents.length; i++){
|
||||
final ItemStack stack = contents[i];
|
||||
if (stack != null && stack.getEnchantmentLevel(Enchantment.THORNS) > 0){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
final PlayerInventory inv = player.getInventory();
|
||||
final ItemStack[] contents = inv.getArmorContents();
|
||||
for (int i = 0; i < contents.length; i++){
|
||||
final ItemStack stack = contents[i];
|
||||
if (stack != null && stack.getEnchantmentLevel(Enchantment.THORNS) > 0){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -402,89 +402,89 @@ public class FightListener extends CheckListener implements JoinLeaveListener{
|
||||
*/
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
|
||||
public void onEntityDamage(final EntityDamageEvent event) {
|
||||
|
||||
final Entity damaged = event.getEntity();
|
||||
final Player damagedPlayer = damaged instanceof Player ? (Player) damaged : null;
|
||||
final FightData damagedData = damagedPlayer == null ? null : FightData.getData(damagedPlayer);
|
||||
final boolean damagedIsDead = damaged.isDead();
|
||||
if (damagedPlayer != null && !damagedIsDead) {
|
||||
|
||||
final Entity damaged = event.getEntity();
|
||||
final Player damagedPlayer = damaged instanceof Player ? (Player) damaged : null;
|
||||
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)){
|
||||
// It requested to "cancel" the players invulnerability, so set their noDamageTicks to 0.
|
||||
damagedPlayer.setNoDamageTicks(0);
|
||||
damagedPlayer.setNoDamageTicks(0);
|
||||
}
|
||||
if (BridgeHealth.getHealth(damagedPlayer) >= BridgeHealth.getMaxHealth(damagedPlayer)){
|
||||
// TODO: Might use the same FightData instance for GodMode.
|
||||
if (damagedData.fastHealBuffer < 0){
|
||||
// Reduce negative buffer with each full health.
|
||||
damagedData.fastHealBuffer /= 2;
|
||||
}
|
||||
// Set reference time.
|
||||
damagedData.fastHealRefTime = System.currentTimeMillis();
|
||||
// TODO: Might use the same FightData instance for GodMode.
|
||||
if (damagedData.fastHealBuffer < 0){
|
||||
// Reduce negative buffer with each full health.
|
||||
damagedData.fastHealBuffer /= 2;
|
||||
}
|
||||
// Set reference time.
|
||||
damagedData.fastHealRefTime = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
// NCPAPIProvider.getNoCheatPlusAPI().getLogManager().debug(LogManager.TRACE_FILE, event.getCause());
|
||||
// Attacking entities.
|
||||
// NCPAPIProvider.getNoCheatPlusAPI().getLogManager().debug(LogManager.TRACE_FILE, event.getCause());
|
||||
// Attacking entities.
|
||||
if (event instanceof EntityDamageByEntityEvent) {
|
||||
final EntityDamageByEntityEvent e = (EntityDamageByEntityEvent) event;
|
||||
final Entity damager = e.getDamager();
|
||||
final int tick = TickTask.getTick();
|
||||
if (damagedPlayer != null && !damagedIsDead){
|
||||
// TODO: check once more when to set this (!) in terms of order.
|
||||
FightData.getData(damagedPlayer).damageTakenByEntityTick = tick;
|
||||
if (damagedPlayer != null && !damagedIsDead){
|
||||
// TODO: check once more when to set this (!) in terms of order.
|
||||
FightData.getData(damagedPlayer).damageTakenByEntityTick = tick;
|
||||
if (hasThorns(damagedPlayer)){
|
||||
// TODO: Cleanup here.
|
||||
// Remember the id of the attacker to allow counter damage.
|
||||
damagedData.thornsId = damager.getEntityId();
|
||||
}
|
||||
else{
|
||||
damagedData.thornsId = Integer.MIN_VALUE;
|
||||
// TODO: Cleanup here.
|
||||
// Remember the id of the attacker to allow counter damage.
|
||||
damagedData.thornsId = damager.getEntityId();
|
||||
}
|
||||
}
|
||||
final DamageCause damageCause = event.getCause();
|
||||
final Player player = damager instanceof Player ? (Player) damager : null;
|
||||
Player attacker = player;
|
||||
// TODO: deobfuscate.
|
||||
if (damager instanceof TNTPrimed) {
|
||||
final Entity source = ((TNTPrimed) damager).getSource();
|
||||
if (source instanceof Player) {
|
||||
attacker = (Player) source;
|
||||
}
|
||||
}
|
||||
if (attacker != null && (damageCause == DamageCause.BLOCK_EXPLOSION || damageCause == DamageCause.ENTITY_EXPLOSION)) {
|
||||
// NOTE: Pigs don't have data.
|
||||
final FightData data = FightData.getData(attacker);
|
||||
data.lastExplosionEntityId = damaged.getEntityId();
|
||||
data.lastExplosionDamageTick = tick;
|
||||
return;
|
||||
}
|
||||
else{
|
||||
damagedData.thornsId = Integer.MIN_VALUE;
|
||||
}
|
||||
}
|
||||
final DamageCause damageCause = event.getCause();
|
||||
final Player player = damager instanceof Player ? (Player) damager : null;
|
||||
Player attacker = player;
|
||||
// TODO: deobfuscate.
|
||||
if (damager instanceof TNTPrimed) {
|
||||
final Entity source = ((TNTPrimed) damager).getSource();
|
||||
if (source instanceof Player) {
|
||||
attacker = (Player) source;
|
||||
}
|
||||
}
|
||||
if (attacker != null && (damageCause == DamageCause.BLOCK_EXPLOSION || damageCause == DamageCause.ENTITY_EXPLOSION)) {
|
||||
// NOTE: Pigs don't have data.
|
||||
final FightData data = FightData.getData(attacker);
|
||||
data.lastExplosionEntityId = damaged.getEntityId();
|
||||
data.lastExplosionDamageTick = tick;
|
||||
return;
|
||||
}
|
||||
if (player != null){
|
||||
final double damage = BridgeHealth.getDamage(e);
|
||||
final FightData data = FightData.getData(player);
|
||||
if (damageCause == DamageCause.ENTITY_ATTACK){
|
||||
// TODO: Might/should skip the damage comparison, though checking on lowest priority.
|
||||
if (damaged.getEntityId() == data.lastExplosionEntityId && tick == data.lastExplosionDamageTick) {
|
||||
data.lastExplosionDamageTick = -1;
|
||||
data.lastExplosionEntityId = Integer.MAX_VALUE;
|
||||
} else if (handleNormalDamage(player, damaged, damage, tick, data)){
|
||||
e.setCancelled(true);
|
||||
}
|
||||
// TODO: Might/should skip the damage comparison, though checking on lowest priority.
|
||||
if (damaged.getEntityId() == data.lastExplosionEntityId && tick == data.lastExplosionDamageTick) {
|
||||
data.lastExplosionDamageTick = -1;
|
||||
data.lastExplosionEntityId = Integer.MAX_VALUE;
|
||||
} else if (handleNormalDamage(player, damaged, damage, tick, data)){
|
||||
e.setCancelled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
|
||||
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){
|
||||
// Plugin compatibility thing.
|
||||
data.lastNoDamageTicks = ndt;
|
||||
}
|
||||
}
|
||||
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){
|
||||
// Plugin compatibility thing.
|
||||
data.lastNoDamageTicks = ndt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -500,7 +500,7 @@ public class FightListener extends CheckListener implements JoinLeaveListener{
|
||||
if (entity instanceof Player){
|
||||
final Player player = (Player) entity;
|
||||
if (godMode.isEnabled(player)) {
|
||||
godMode.death(player);
|
||||
godMode.death(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -526,67 +526,67 @@ public class FightListener extends CheckListener implements JoinLeaveListener{
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
|
||||
public void onPlayerToggleSprint(final PlayerToggleSprintEvent event) {
|
||||
if (event.isSprinting()) {
|
||||
FightData.getData(event.getPlayer()).knockbackSprintTime = System.currentTimeMillis();
|
||||
FightData.getData(event.getPlayer()).knockbackSprintTime = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
||||
public void onEntityRegainHealthLow(final EntityRegainHealthEvent event){
|
||||
final Entity entity = event.getEntity();
|
||||
if (!(entity instanceof Player)) return;
|
||||
final Player player = (Player) entity;
|
||||
if (player.isDead() && BridgeHealth.getHealth(player) <= 0.0) {
|
||||
// Heal after death.
|
||||
event.setCancelled(true);
|
||||
counters.addPrimaryThread(idCancelDead, 1);
|
||||
return;
|
||||
}
|
||||
if (event.getRegainReason() != RegainReason.SATIATED) {
|
||||
return;
|
||||
}
|
||||
if (fastHeal.isEnabled(player) && fastHeal.check(player)) {
|
||||
// TODO: Can clients force events with 0-re-gain ?
|
||||
event.setCancelled(true);
|
||||
}
|
||||
final Entity entity = event.getEntity();
|
||||
if (!(entity instanceof Player)) return;
|
||||
final Player player = (Player) entity;
|
||||
if (player.isDead() && BridgeHealth.getHealth(player) <= 0.0) {
|
||||
// Heal after death.
|
||||
event.setCancelled(true);
|
||||
counters.addPrimaryThread(idCancelDead, 1);
|
||||
return;
|
||||
}
|
||||
if (event.getRegainReason() != RegainReason.SATIATED) {
|
||||
return;
|
||||
}
|
||||
if (fastHeal.isEnabled(player) && fastHeal.check(player)) {
|
||||
// TODO: Can clients force events with 0-re-gain ?
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onEntityRegainHealth(final EntityRegainHealthEvent event){
|
||||
final Entity entity = event.getEntity();
|
||||
if (!(entity instanceof Player)) return;
|
||||
final Player player = (Player) entity;
|
||||
final FightData data = FightData.getData(player);
|
||||
// Adjust god mode data:
|
||||
// Remember the time.
|
||||
data.regainHealthTime = System.currentTimeMillis();
|
||||
// Set god-mode health to maximum.
|
||||
// TODO: Mind that health regain might half the ndt.
|
||||
final double health = Math.min(BridgeHealth.getHealth(player) + BridgeHealth.getAmount(event), BridgeHealth.getMaxHealth(player));
|
||||
data.godModeHealth = Math.max(data.godModeHealth, health);
|
||||
final Entity entity = event.getEntity();
|
||||
if (!(entity instanceof Player)) return;
|
||||
final Player player = (Player) entity;
|
||||
final FightData data = FightData.getData(player);
|
||||
// Adjust god mode data:
|
||||
// Remember the time.
|
||||
data.regainHealthTime = System.currentTimeMillis();
|
||||
// Set god-mode health to maximum.
|
||||
// TODO: Mind that health regain might half the ndt.
|
||||
final double health = Math.min(BridgeHealth.getHealth(player) + BridgeHealth.getAmount(event), BridgeHealth.getMaxHealth(player));
|
||||
data.godModeHealth = Math.max(data.godModeHealth, health);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playerJoins(final Player player) {
|
||||
}
|
||||
@Override
|
||||
public void playerJoins(final Player player) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playerLeaves(final Player player) {
|
||||
final FightData data = FightData.getData(player);
|
||||
data.angleHits.clear();
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
@Override
|
||||
public void playerLeaves(final Player player) {
|
||||
final FightData data = FightData.getData(player);
|
||||
data.angleHits.clear();
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onPlayerChangedWorld(final PlayerChangedWorldEvent event){
|
||||
FightData.getData(event.getPlayer()).onWorldChange();
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = false, priority = EventPriority.MONITOR)
|
||||
FightData.getData(event.getPlayer()).onWorldChange();
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
final Player player = event.getPlayer();
|
||||
final long penalty = FightConfig.getConfig(player).toolChangeAttackPenalty;
|
||||
if (penalty > 0 ) {
|
||||
FightData.getData(player).attackPenalty.applyPenalty(penalty);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ public class Reach extends Check {
|
||||
|
||||
/** The maximum distance allowed to interact with an entity in creative mode. */
|
||||
public static final double CREATIVE_DISTANCE = 6D;
|
||||
|
||||
|
||||
/** Additum for distance, based on entity. */
|
||||
private static double getDistMod(final Entity damaged) {
|
||||
// Handle the EnderDragon differently.
|
||||
@ -55,7 +55,7 @@ public class Reach extends Check {
|
||||
*/
|
||||
public boolean check(final Player player, final Location pLoc, final Entity damaged, final Location dRef, final FightData data, final FightConfig cc) {
|
||||
boolean cancel = false;
|
||||
|
||||
|
||||
// The maximum distance allowed to interact with an entity in survival mode.
|
||||
final double SURVIVAL_DISTANCE = cc.reachSurvivalDistance; // 4.4D;
|
||||
// Amount which can be reduced by reach adaption.
|
||||
@ -65,9 +65,9 @@ public class Reach extends Check {
|
||||
|
||||
final double distanceLimit = player.getGameMode() == GameMode.CREATIVE ? CREATIVE_DISTANCE : SURVIVAL_DISTANCE + getDistMod(damaged);
|
||||
final double distanceMin = (distanceLimit - DYNAMIC_RANGE) / distanceLimit;
|
||||
|
||||
|
||||
final double height = mcAccess.getHeight(damaged);
|
||||
|
||||
|
||||
// Refine y position.
|
||||
// TODO: Make a little more accurate by counting in the actual bounding box.
|
||||
final double pY = pLoc.getY() + player.getEyeHeight();
|
||||
@ -75,29 +75,29 @@ public class Reach extends Check {
|
||||
if (pY <= dY); // Keep the foot level y.
|
||||
else if (pY >= dY + height) dRef.setY(dY + height); // Highest ref y.
|
||||
else dRef.setY(pY); // Level with damaged.
|
||||
|
||||
|
||||
final Vector pRel = dRef.toVector().subtract(pLoc.toVector().setY(pY)); // TODO: Run calculations on numbers only :p.
|
||||
|
||||
|
||||
// Distance is calculated from eye location to center of targeted. If the player is further away from their target
|
||||
// than allowed, the difference will be assigned to "distance".
|
||||
final double lenpRel = pRel.length();
|
||||
|
||||
|
||||
double violation = lenpRel - distanceLimit;
|
||||
|
||||
|
||||
final double reachMod = data.reachMod;
|
||||
|
||||
if (violation > 0) {
|
||||
// They failed, increment violation level. This is influenced by lag, so don't do it if there was lag.
|
||||
if (TickTask.getLag(1000, true) < 1.5f){
|
||||
// TODO: 1.5 is a fantasy value.
|
||||
data.reachVL += violation;
|
||||
// TODO: 1.5 is a fantasy value.
|
||||
data.reachVL += violation;
|
||||
}
|
||||
|
||||
// Execute whatever actions are associated with this check and the violation level and find out if we should
|
||||
// cancel the event.
|
||||
cancel = executeActions(player, data.reachVL, violation, cc.reachActions);
|
||||
if (Improbable.check(player, (float) violation / 2f, System.currentTimeMillis(), "fight.reach")){
|
||||
cancel = true;
|
||||
cancel = true;
|
||||
}
|
||||
if (cancel && cc.reachPenalty > 0){
|
||||
// Apply an attack penalty time.
|
||||
@ -105,36 +105,36 @@ public class Reach extends Check {
|
||||
}
|
||||
}
|
||||
else if (lenpRel - distanceLimit * reachMod > 0){
|
||||
// Silent cancel.
|
||||
if (cc.reachPenalty > 0) {
|
||||
data.attackPenalty.applyPenalty(cc.reachPenalty / 2);
|
||||
}
|
||||
// Silent cancel.
|
||||
if (cc.reachPenalty > 0) {
|
||||
data.attackPenalty.applyPenalty(cc.reachPenalty / 2);
|
||||
}
|
||||
cancel = true;
|
||||
Improbable.feed(player, (float) (lenpRel - distanceLimit * reachMod) / 4f, System.currentTimeMillis());
|
||||
}
|
||||
else{
|
||||
// Player passed the check, reward them.
|
||||
data.reachVL *= 0.8D;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (!cc.reachReduce){
|
||||
data.reachMod = 1d;
|
||||
data.reachMod = 1d;
|
||||
}
|
||||
else if (lenpRel > distanceLimit - DYNAMIC_RANGE){
|
||||
data.reachMod = Math.max(distanceMin, data.reachMod - DYNAMIC_STEP);
|
||||
data.reachMod = Math.max(distanceMin, data.reachMod - DYNAMIC_STEP);
|
||||
}
|
||||
else{
|
||||
data.reachMod = Math.min(1.0, data.reachMod + DYNAMIC_STEP);
|
||||
data.reachMod = Math.min(1.0, data.reachMod + DYNAMIC_STEP);
|
||||
}
|
||||
|
||||
|
||||
if (cc.debug && player.hasPermission(Permissions.ADMINISTRATION_DEBUG)){
|
||||
player.sendMessage("NC+: Attack/reach " + damaged.getType()+ " height="+ StringUtil.fdec3.format(height) + " dist=" + StringUtil.fdec3.format(lenpRel) +" @" + StringUtil.fdec3.format(reachMod));
|
||||
}
|
||||
|
||||
return cancel;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Data context for iterating over TraceEntry instances.
|
||||
* @param player
|
||||
@ -145,93 +145,93 @@ public class Reach extends Check {
|
||||
* @param cc
|
||||
* @return
|
||||
*/
|
||||
public ReachContext getContext(final Player player, final Location pLoc, final Entity damaged, final Location damagedLoc, final FightData data, final FightConfig cc) {
|
||||
final ReachContext context = new ReachContext();
|
||||
context.distanceLimit = player.getGameMode() == GameMode.CREATIVE ? CREATIVE_DISTANCE : cc.reachSurvivalDistance + getDistMod(damaged);
|
||||
public ReachContext getContext(final Player player, final Location pLoc, final Entity damaged, final Location damagedLoc, final FightData data, final FightConfig cc) {
|
||||
final ReachContext context = new ReachContext();
|
||||
context.distanceLimit = player.getGameMode() == GameMode.CREATIVE ? CREATIVE_DISTANCE : cc.reachSurvivalDistance + getDistMod(damaged);
|
||||
context.distanceMin = (context.distanceLimit - cc.reachReduceDistance) / context.distanceLimit;
|
||||
context.damagedHeight = mcAccess.getHeight(damaged);
|
||||
//context.eyeHeight = player.getEyeHeight();
|
||||
context.pY = pLoc.getY() + player.getEyeHeight();
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the player fails the reach check, no change of FightData.
|
||||
* @param player
|
||||
* @param pLoc
|
||||
* @param damaged
|
||||
* @param dRef
|
||||
* @param context
|
||||
* @param data
|
||||
* @param cc
|
||||
* @return
|
||||
*/
|
||||
public boolean loopCheck(final Player player, final Location pLoc, final Entity damaged, final TraceEntry dRef, final ReachContext context, final FightData data, final FightConfig cc) {
|
||||
boolean cancel = false;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the player fails the reach check, no change of FightData.
|
||||
* @param player
|
||||
* @param pLoc
|
||||
* @param damaged
|
||||
* @param dRef
|
||||
* @param context
|
||||
* @param data
|
||||
* @param cc
|
||||
* @return
|
||||
*/
|
||||
public boolean loopCheck(final Player player, final Location pLoc, final Entity damaged, final TraceEntry dRef, final ReachContext context, final FightData data, final FightConfig cc) {
|
||||
boolean cancel = false;
|
||||
|
||||
// Refine y position.
|
||||
final double dY = dRef.y;
|
||||
double y = dRef.y;
|
||||
|
||||
|
||||
if (context.pY <= dY) {
|
||||
// Keep the foot level y.
|
||||
// Keep the foot level y.
|
||||
}
|
||||
else if (context.pY >= dY + context.damagedHeight) {
|
||||
y = dY + context.damagedHeight; // Highest ref y.
|
||||
y = dY + context.damagedHeight; // Highest ref y.
|
||||
}
|
||||
else {
|
||||
y = context.pY; // Level with damaged.
|
||||
y = context.pY; // Level with damaged.
|
||||
}
|
||||
|
||||
|
||||
// Distance is calculated from eye location to center of targeted. If the player is further away from their target
|
||||
// than allowed, the difference will be assigned to "distance".
|
||||
// TODO: Run check on squared distances (quite easy to change to stored boundary-sq values).
|
||||
final double lenpRel = TrigUtil.distance(dRef.x, y, dRef.z, pLoc.getX(), context.pY, pLoc.getZ());
|
||||
|
||||
|
||||
double violation = lenpRel - context.distanceLimit;
|
||||
|
||||
|
||||
if (violation > 0 || lenpRel - context.distanceLimit * data.reachMod > 0){
|
||||
// TODO: The silent cancel parts should be sen as "no violation" ?
|
||||
// Set minimum violation in context
|
||||
context.minViolation = Math.min(context.minViolation, lenpRel);
|
||||
cancel = true;
|
||||
// TODO: The silent cancel parts should be sen as "no violation" ?
|
||||
// Set minimum violation in context
|
||||
context.minViolation = Math.min(context.minViolation, lenpRel);
|
||||
cancel = true;
|
||||
}
|
||||
context.minResult = Math.min(context.minResult, lenpRel);
|
||||
|
||||
return cancel;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply changes to FightData according to check results (context), trigger violations.
|
||||
* @param player
|
||||
* @param pLoc
|
||||
* @param damaged
|
||||
* @param context
|
||||
* @param forceViolation
|
||||
* @param data
|
||||
* @param cc
|
||||
* @return
|
||||
*/
|
||||
public boolean loopFinish(final Player player, final Location pLoc, final Entity damaged, final ReachContext context, final boolean forceViolation, final FightData data, final FightConfig cc) {
|
||||
final double lenpRel = forceViolation && context.minViolation != Double.MAX_VALUE ? context.minViolation : context.minResult;
|
||||
if (lenpRel == Double.MAX_VALUE) {
|
||||
return false;
|
||||
}
|
||||
double violation = lenpRel - context.distanceLimit;
|
||||
boolean cancel = false;
|
||||
if (violation > 0) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply changes to FightData according to check results (context), trigger violations.
|
||||
* @param player
|
||||
* @param pLoc
|
||||
* @param damaged
|
||||
* @param context
|
||||
* @param forceViolation
|
||||
* @param data
|
||||
* @param cc
|
||||
* @return
|
||||
*/
|
||||
public boolean loopFinish(final Player player, final Location pLoc, final Entity damaged, final ReachContext context, final boolean forceViolation, final FightData data, final FightConfig cc) {
|
||||
final double lenpRel = forceViolation && context.minViolation != Double.MAX_VALUE ? context.minViolation : context.minResult;
|
||||
if (lenpRel == Double.MAX_VALUE) {
|
||||
return false;
|
||||
}
|
||||
double violation = lenpRel - context.distanceLimit;
|
||||
boolean cancel = false;
|
||||
if (violation > 0) {
|
||||
// They failed, increment violation level. This is influenced by lag, so don't do it if there was lag.
|
||||
if (TickTask.getLag(1000, true) < 1.5f){
|
||||
// TODO: 1.5 is a fantasy value.
|
||||
data.reachVL += violation;
|
||||
// TODO: 1.5 is a fantasy value.
|
||||
data.reachVL += violation;
|
||||
}
|
||||
|
||||
// Execute whatever actions are associated with this check and the violation level and find out if we should
|
||||
// cancel the event.
|
||||
cancel = executeActions(player, data.reachVL, violation, cc.reachActions);
|
||||
if (Improbable.check(player, (float) violation / 2f, System.currentTimeMillis(), "fight.reach")){
|
||||
cancel = true;
|
||||
cancel = true;
|
||||
}
|
||||
if (cancel && cc.reachPenalty > 0){
|
||||
// Apply an attack penalty time.
|
||||
@ -239,34 +239,34 @@ public class Reach extends Check {
|
||||
}
|
||||
}
|
||||
else if (lenpRel - context.distanceLimit * data.reachMod > 0){
|
||||
// Silent cancel.
|
||||
if (cc.reachPenalty > 0) {
|
||||
data.attackPenalty.applyPenalty(cc.reachPenalty / 2);
|
||||
}
|
||||
// Silent cancel.
|
||||
if (cc.reachPenalty > 0) {
|
||||
data.attackPenalty.applyPenalty(cc.reachPenalty / 2);
|
||||
}
|
||||
cancel = true;
|
||||
Improbable.feed(player, (float) (lenpRel - context.distanceLimit * data.reachMod) / 4f, System.currentTimeMillis());
|
||||
}
|
||||
else{
|
||||
// Player passed the check, reward them.
|
||||
data.reachVL *= 0.8D;
|
||||
|
||||
|
||||
}
|
||||
// Adaption amount for dynamic range.
|
||||
final double DYNAMIC_STEP = cc.reachReduceStep / cc.reachSurvivalDistance;
|
||||
if (!cc.reachReduce){
|
||||
data.reachMod = 1d;
|
||||
data.reachMod = 1d;
|
||||
}
|
||||
else if (lenpRel > context.distanceLimit - cc.reachReduceDistance){
|
||||
data.reachMod = Math.max(context.distanceMin, data.reachMod - DYNAMIC_STEP);
|
||||
data.reachMod = Math.max(context.distanceMin, data.reachMod - DYNAMIC_STEP);
|
||||
}
|
||||
else{
|
||||
data.reachMod = Math.min(1.0, data.reachMod + DYNAMIC_STEP);
|
||||
data.reachMod = Math.min(1.0, data.reachMod + DYNAMIC_STEP);
|
||||
}
|
||||
|
||||
|
||||
if (cc.debug && player.hasPermission(Permissions.ADMINISTRATION_DEBUG)){
|
||||
player.sendMessage("NC+: Attack/reach " + damaged.getType()+ " height="+ StringUtil.fdec3.format(context.damagedHeight) + " dist=" + StringUtil.fdec3.format(lenpRel) +" @" + StringUtil.fdec3.format(data.reachMod));
|
||||
}
|
||||
|
||||
return cancel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,17 +6,17 @@ package fr.neatmonster.nocheatplus.checks.fight;
|
||||
*
|
||||
*/
|
||||
public class ReachContext {
|
||||
|
||||
public double distanceLimit;
|
||||
public double distanceMin;
|
||||
public double damagedHeight;
|
||||
/** Attacking player. */
|
||||
public double eyeHeight;
|
||||
/** Eye location y of the attacking player. */
|
||||
public double pY;
|
||||
/** Minimum value of lenpRel that was a violation. */
|
||||
public double minViolation = Double.MAX_VALUE;
|
||||
/** Minimum value of lenpRel. */
|
||||
public double minResult = Double.MAX_VALUE;
|
||||
|
||||
public double distanceLimit;
|
||||
public double distanceMin;
|
||||
public double damagedHeight;
|
||||
/** Attacking player. */
|
||||
public double eyeHeight;
|
||||
/** Eye location y of the attacking player. */
|
||||
public double pY;
|
||||
/** Minimum value of lenpRel that was a violation. */
|
||||
public double minViolation = Double.MAX_VALUE;
|
||||
/** Minimum value of lenpRel. */
|
||||
public double minResult = Double.MAX_VALUE;
|
||||
|
||||
}
|
||||
|
@ -17,226 +17,225 @@ import fr.neatmonster.nocheatplus.utilities.TrigUtil;
|
||||
*
|
||||
*/
|
||||
public class LocationTrace {
|
||||
|
||||
public static final class TraceEntry {
|
||||
|
||||
/** We keep it open, if ticks or ms are used. */
|
||||
public long time;
|
||||
/** Coordinates. */
|
||||
public double x, y, z;
|
||||
public double lastDistSq;
|
||||
|
||||
public void set(long time, double x, double y, double z, double lastDistSq) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.time = time;
|
||||
this.lastDistSq = lastDistSq;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate from oldest to latest. Not a fully featured Iterator.
|
||||
* @author mc_dev
|
||||
*
|
||||
*/
|
||||
public static final class TraceIterator implements Iterator<TraceEntry>{
|
||||
private final TraceEntry[] entries;
|
||||
/** Index as in LocationTrace */
|
||||
private final int index;
|
||||
private final int size;
|
||||
private int currentIndex;
|
||||
private final boolean ascend;
|
||||
|
||||
protected TraceIterator(TraceEntry[] entries, int index, int size, int currentIndex, boolean ascend) {
|
||||
if (currentIndex >= entries.length || currentIndex < 0 ||
|
||||
currentIndex <= index - size || currentIndex > index && currentIndex <= index - size + entries.length) {
|
||||
// This should also prevent iterators for size == 0, for the moment (!).
|
||||
throw new IllegalArgumentException("startIndex out of bounds.");
|
||||
}
|
||||
this.entries = entries;
|
||||
this.index = index;
|
||||
this.size = size;
|
||||
this.currentIndex = currentIndex;
|
||||
this.ascend = ascend;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final TraceEntry next() {
|
||||
if (!hasNext()) {
|
||||
throw new IndexOutOfBoundsException("No more entries to iterate.");
|
||||
}
|
||||
final TraceEntry entry = entries[currentIndex];
|
||||
if (ascend) {
|
||||
currentIndex ++;
|
||||
if (currentIndex >= entries.length) {
|
||||
currentIndex = 0;
|
||||
}
|
||||
int ref = index - size + 1;
|
||||
if (ref < 0) {
|
||||
ref += entries.length;
|
||||
}
|
||||
if (currentIndex == ref) {
|
||||
// Invalidate the iterator.
|
||||
currentIndex = -1;
|
||||
}
|
||||
} else {
|
||||
currentIndex --;
|
||||
if (currentIndex < 0) {
|
||||
currentIndex = entries.length - 1;
|
||||
}
|
||||
if (currentIndex == index) {
|
||||
// Invalidate the iterator.
|
||||
currentIndex = - 1;
|
||||
}
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean hasNext() {
|
||||
// Just check if currentIndex is within range.
|
||||
return currentIndex >= 0 && currentIndex <= index && currentIndex > index - size || currentIndex > index && currentIndex >= index - size + entries.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** A Ring. */
|
||||
private final TraceEntry[] entries;
|
||||
/** Last element index. */
|
||||
private int index = -1;
|
||||
/** Number of valid entries. */
|
||||
private int size = 0;
|
||||
private final double mergeDist;
|
||||
private final double mergeDistSq;
|
||||
|
||||
// (No world name stored: Should be reset on world changes.)
|
||||
|
||||
public LocationTrace(int bufferSize, double mergeDist) {
|
||||
// TODO: Might consider a cut-off distance/age (performance saving for iteration).
|
||||
if (bufferSize < 1) {
|
||||
throw new IllegalArgumentException("Expect bufferSize > 0, got instead: " + bufferSize);
|
||||
}
|
||||
entries = new TraceEntry[bufferSize];
|
||||
for (int i = 0; i < bufferSize; i++) {
|
||||
entries[i] = new TraceEntry();
|
||||
}
|
||||
this.mergeDist = mergeDist;
|
||||
this.mergeDistSq = mergeDist * mergeDist;
|
||||
}
|
||||
|
||||
public final void addEntry(final long time, final double x, final double y, final double z) {
|
||||
double lastDistSq = 0.0;
|
||||
if (size > 0) {
|
||||
final TraceEntry latestEntry = entries[index];
|
||||
// TODO: Consider duration of staying there ?
|
||||
if (x == latestEntry.x && y == latestEntry.y && z == latestEntry.z) {
|
||||
latestEntry.time = time;
|
||||
return;
|
||||
}
|
||||
lastDistSq = TrigUtil.distanceSquared(x, y, z, latestEntry.x, latestEntry.y, latestEntry.z);
|
||||
// TODO: Think about minMergeSize (1 = never merge the first two, size = first fill the ring).
|
||||
if (size > 1 && lastDistSq <= mergeDistSq) {
|
||||
// TODO: Could use Manhattan, after all.
|
||||
// Only merge if last distance was not greater than mergeDist, to prevent too-far-off entries.
|
||||
if (latestEntry.lastDistSq <= mergeDistSq) {
|
||||
// Update lastDistSq, due to shifting the elements position.
|
||||
final TraceEntry secondLatest = index - 1 < 0 ? entries[index - 1 + entries.length] : entries[index - 1];
|
||||
lastDistSq = TrigUtil.distanceSquared(x, y, z, secondLatest.x, secondLatest.y, secondLatest.z);
|
||||
latestEntry.set(time, x, y, z, lastDistSq);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Advance index.
|
||||
index++;
|
||||
if (index == entries.length) {
|
||||
index = 0;
|
||||
}
|
||||
if (size < entries.length) {
|
||||
size ++;
|
||||
}
|
||||
final TraceEntry newEntry = entries[index];
|
||||
newEntry.set(time, x, y, z, lastDistSq);
|
||||
}
|
||||
|
||||
/** Reset content pointers - call with world changes. */
|
||||
public void reset() {
|
||||
index = 0;
|
||||
size = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the actual number of valid elements. After some time of moving this should be entries.length.
|
||||
* @return
|
||||
*/
|
||||
public int size() {
|
||||
return size;
|
||||
}
|
||||
public boolean isEmpty() {
|
||||
return size == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get size of ring buffer (maximal possible number of elements).
|
||||
* @return
|
||||
*/
|
||||
public int getMaxSize() {
|
||||
return entries.length;
|
||||
}
|
||||
|
||||
public double getMergeDist() {
|
||||
return mergeDist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate from latest to oldest.
|
||||
* @return
|
||||
*/
|
||||
public TraceIterator latestIterator() {
|
||||
return new TraceIterator(entries, index, size, index, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate from oldest to latest.
|
||||
* @return
|
||||
*/
|
||||
public TraceIterator oldestIterator() {
|
||||
final int currentIndex = index - size + 1;
|
||||
return new TraceIterator(entries, index, size, currentIndex < 0 ? currentIndex + entries.length : currentIndex, true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Iterate from entry with max. age to latest, always includes latest.
|
||||
* @param tick Absolute tick value for oldest accepted tick.
|
||||
* @param ms Absolute ms value for oldest accepted ms;
|
||||
* @return
|
||||
*/
|
||||
public TraceIterator maxAgeIterator(long time) {
|
||||
int currentIndex = index;
|
||||
int tempIndex = currentIndex;
|
||||
int steps = 1;
|
||||
while (steps < size) {
|
||||
tempIndex --;
|
||||
if (tempIndex < 0) {
|
||||
tempIndex += size;
|
||||
}
|
||||
final TraceEntry entry = entries[tempIndex];
|
||||
if (entry.time >= time) {
|
||||
// Continue.
|
||||
currentIndex = tempIndex;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
steps ++;
|
||||
}
|
||||
return new TraceIterator(entries, index, size, currentIndex, true);
|
||||
}
|
||||
|
||||
|
||||
public static final class TraceEntry {
|
||||
|
||||
/** We keep it open, if ticks or ms are used. */
|
||||
public long time;
|
||||
/** Coordinates. */
|
||||
public double x, y, z;
|
||||
public double lastDistSq;
|
||||
|
||||
public void set(long time, double x, double y, double z, double lastDistSq) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.time = time;
|
||||
this.lastDistSq = lastDistSq;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate from oldest to latest. Not a fully featured Iterator.
|
||||
* @author mc_dev
|
||||
*
|
||||
*/
|
||||
public static final class TraceIterator implements Iterator<TraceEntry>{
|
||||
private final TraceEntry[] entries;
|
||||
/** Index as in LocationTrace */
|
||||
private final int index;
|
||||
private final int size;
|
||||
private int currentIndex;
|
||||
private final boolean ascend;
|
||||
|
||||
protected TraceIterator(TraceEntry[] entries, int index, int size, int currentIndex, boolean ascend) {
|
||||
if (currentIndex >= entries.length || currentIndex < 0 ||
|
||||
currentIndex <= index - size || currentIndex > index && currentIndex <= index - size + entries.length) {
|
||||
// This should also prevent iterators for size == 0, for the moment (!).
|
||||
throw new IllegalArgumentException("startIndex out of bounds.");
|
||||
}
|
||||
this.entries = entries;
|
||||
this.index = index;
|
||||
this.size = size;
|
||||
this.currentIndex = currentIndex;
|
||||
this.ascend = ascend;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final TraceEntry next() {
|
||||
if (!hasNext()) {
|
||||
throw new IndexOutOfBoundsException("No more entries to iterate.");
|
||||
}
|
||||
final TraceEntry entry = entries[currentIndex];
|
||||
if (ascend) {
|
||||
currentIndex ++;
|
||||
if (currentIndex >= entries.length) {
|
||||
currentIndex = 0;
|
||||
}
|
||||
int ref = index - size + 1;
|
||||
if (ref < 0) {
|
||||
ref += entries.length;
|
||||
}
|
||||
if (currentIndex == ref) {
|
||||
// Invalidate the iterator.
|
||||
currentIndex = -1;
|
||||
}
|
||||
} else {
|
||||
currentIndex --;
|
||||
if (currentIndex < 0) {
|
||||
currentIndex = entries.length - 1;
|
||||
}
|
||||
if (currentIndex == index) {
|
||||
// Invalidate the iterator.
|
||||
currentIndex = - 1;
|
||||
}
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean hasNext() {
|
||||
// Just check if currentIndex is within range.
|
||||
return currentIndex >= 0 && currentIndex <= index && currentIndex > index - size || currentIndex > index && currentIndex >= index - size + entries.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** A Ring. */
|
||||
private final TraceEntry[] entries;
|
||||
/** Last element index. */
|
||||
private int index = -1;
|
||||
/** Number of valid entries. */
|
||||
private int size = 0;
|
||||
private final double mergeDist;
|
||||
private final double mergeDistSq;
|
||||
|
||||
// (No world name stored: Should be reset on world changes.)
|
||||
|
||||
public LocationTrace(int bufferSize, double mergeDist) {
|
||||
// TODO: Might consider a cut-off distance/age (performance saving for iteration).
|
||||
if (bufferSize < 1) {
|
||||
throw new IllegalArgumentException("Expect bufferSize > 0, got instead: " + bufferSize);
|
||||
}
|
||||
entries = new TraceEntry[bufferSize];
|
||||
for (int i = 0; i < bufferSize; i++) {
|
||||
entries[i] = new TraceEntry();
|
||||
}
|
||||
this.mergeDist = mergeDist;
|
||||
this.mergeDistSq = mergeDist * mergeDist;
|
||||
}
|
||||
|
||||
public final void addEntry(final long time, final double x, final double y, final double z) {
|
||||
double lastDistSq = 0.0;
|
||||
if (size > 0) {
|
||||
final TraceEntry latestEntry = entries[index];
|
||||
// TODO: Consider duration of staying there ?
|
||||
if (x == latestEntry.x && y == latestEntry.y && z == latestEntry.z) {
|
||||
latestEntry.time = time;
|
||||
return;
|
||||
}
|
||||
lastDistSq = TrigUtil.distanceSquared(x, y, z, latestEntry.x, latestEntry.y, latestEntry.z);
|
||||
// TODO: Think about minMergeSize (1 = never merge the first two, size = first fill the ring).
|
||||
if (size > 1 && lastDistSq <= mergeDistSq) {
|
||||
// TODO: Could use Manhattan, after all.
|
||||
// Only merge if last distance was not greater than mergeDist, to prevent too-far-off entries.
|
||||
if (latestEntry.lastDistSq <= mergeDistSq) {
|
||||
// Update lastDistSq, due to shifting the elements position.
|
||||
final TraceEntry secondLatest = index - 1 < 0 ? entries[index - 1 + entries.length] : entries[index - 1];
|
||||
lastDistSq = TrigUtil.distanceSquared(x, y, z, secondLatest.x, secondLatest.y, secondLatest.z);
|
||||
latestEntry.set(time, x, y, z, lastDistSq);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Advance index.
|
||||
index++;
|
||||
if (index == entries.length) {
|
||||
index = 0;
|
||||
}
|
||||
if (size < entries.length) {
|
||||
size ++;
|
||||
}
|
||||
final TraceEntry newEntry = entries[index];
|
||||
newEntry.set(time, x, y, z, lastDistSq);
|
||||
}
|
||||
|
||||
/** Reset content pointers - call with world changes. */
|
||||
public void reset() {
|
||||
index = 0;
|
||||
size = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the actual number of valid elements. After some time of moving this should be entries.length.
|
||||
* @return
|
||||
*/
|
||||
public int size() {
|
||||
return size;
|
||||
}
|
||||
public boolean isEmpty() {
|
||||
return size == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get size of ring buffer (maximal possible number of elements).
|
||||
* @return
|
||||
*/
|
||||
public int getMaxSize() {
|
||||
return entries.length;
|
||||
}
|
||||
|
||||
public double getMergeDist() {
|
||||
return mergeDist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate from latest to oldest.
|
||||
* @return
|
||||
*/
|
||||
public TraceIterator latestIterator() {
|
||||
return new TraceIterator(entries, index, size, index, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate from oldest to latest.
|
||||
* @return
|
||||
*/
|
||||
public TraceIterator oldestIterator() {
|
||||
final int currentIndex = index - size + 1;
|
||||
return new TraceIterator(entries, index, size, currentIndex < 0 ? currentIndex + entries.length : currentIndex, true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Iterate from entry with max. age to latest, always includes latest.
|
||||
* @param time Absolute time value for oldest accepted entries.
|
||||
* @return TraceIterator containing entries that have not been created before the given time, iterating ascending with time.
|
||||
*/
|
||||
public TraceIterator maxAgeIterator(long time) {
|
||||
int currentIndex = index;
|
||||
int tempIndex = currentIndex;
|
||||
int steps = 1;
|
||||
while (steps < size) {
|
||||
tempIndex --;
|
||||
if (tempIndex < 0) {
|
||||
tempIndex += size;
|
||||
}
|
||||
final TraceEntry entry = entries[tempIndex];
|
||||
if (entry.time >= time) {
|
||||
// Continue.
|
||||
currentIndex = tempIndex;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
steps ++;
|
||||
}
|
||||
return new TraceIterator(entries, index, size, currentIndex, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -131,7 +131,7 @@ public class MovingData extends ACheckData {
|
||||
public double fromX = Double.MAX_VALUE, fromY, fromZ;
|
||||
/** Last to coordinates. */
|
||||
public double toX = Double.MAX_VALUE, toY, toZ;
|
||||
/** Moving trace (to positions). This is initialized on "playerJoins, i.e. MONITOR, and set to null on playerLeaves."*/
|
||||
/** Moving trace (to-positions, use tick as time). This is initialized on "playerJoins, i.e. MONITOR, and set to null on playerLeaves."*/
|
||||
private LocationTrace trace = null;
|
||||
|
||||
// sf rather
|
||||
|
Loading…
Reference in New Issue
Block a user