Quickly add box margins to LocationTrace/ITraceElement.

* Remove SharedContext.
This commit is contained in:
asofold 2016-06-20 22:07:53 +02:00
parent 65525f3c14
commit 4653b46579
10 changed files with 108 additions and 100 deletions

View File

@ -117,21 +117,17 @@ public class Direction extends Check {
*/
public DirectionContext getContext(final Player player, final Location loc,
final Entity damaged, final boolean damagedIsFake, final Location damagedLoc,
final FightData data, final FightConfig cc, final SharedContext sharedContext) {
final FightData data, final FightConfig cc) {
final DirectionContext context = new DirectionContext();
// Find out how wide the entity is.
if (damagedIsFake) {
// Assume player / default.
context.damagedComplex = false; // Later prefer bukkit based provider.
context.damagedWidth = 0.6;
}
else {
final MCAccess mcAccess = this.mcAccess.getHandle();
context.damagedComplex = mcAccess.isComplexPart(damaged);
context.damagedWidth = mcAccess.getWidth(damaged);
}
// entity.height is broken and will always be 0, therefore. Calculate height instead based on boundingBox.
context.damagedHeight = sharedContext.damagedHeight;
context.direction = loc.getDirection();
context.lengthDirection = context.direction.length();
return context;
@ -161,19 +157,20 @@ public class Direction extends Check {
// 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 damagedBoxMarginHorizontal = dLoc.getBoxMarginHorizontal();
final double damagedBoxMarginVertical = dLoc.getBoxMarginVertical();
final double off;
if (cc.directionStrict){
off = TrigUtil.combinedDirectionCheck(loc, player.getEyeHeight(), context.direction, dLoc.getX(), dLoc.getY() + context.damagedHeight / 2D, dLoc.getZ(), context.damagedWidth, context.damagedHeight, TrigUtil.DIRECTION_LOOP_PRECISION, 80.0);
off = TrigUtil.combinedDirectionCheck(loc, player.getEyeHeight(), context.direction, dLoc.getX(), dLoc.getY() + damagedBoxMarginVertical / 2D, dLoc.getZ(), damagedBoxMarginHorizontal * 2.0, damagedBoxMarginVertical, TrigUtil.DIRECTION_LOOP_PRECISION, 80.0);
}
else{
// Also take into account the angle.
off = TrigUtil.directionCheck(loc, player.getEyeHeight(), context.direction, dLoc.getX(), dLoc.getY() + context.damagedHeight / 2D, dLoc.getZ(), context.damagedWidth, context.damagedHeight, TrigUtil.DIRECTION_LOOP_PRECISION);
off = TrigUtil.directionCheck(loc, player.getEyeHeight(), context.direction, dLoc.getX(), dLoc.getY() + damagedBoxMarginVertical / 2D, dLoc.getZ(), damagedBoxMarginHorizontal * 2.0, damagedBoxMarginVertical, TrigUtil.DIRECTION_LOOP_PRECISION);
}
if (off > 0.1) {
// Player failed the check. Let's try to guess how far they were from looking directly to the entity...
final Vector blockEyes = new Vector(dLoc.getX() - loc.getX(), dLoc.getY() + context.damagedHeight / 2D - loc.getY() - player.getEyeHeight(), dLoc.getZ() - loc.getZ());
final Vector blockEyes = new Vector(dLoc.getX() - loc.getX(), dLoc.getY() + damagedBoxMarginVertical / 2D - loc.getY() - player.getEyeHeight(), dLoc.getZ() - loc.getZ());
final double distance = blockEyes.crossProduct(context.direction).length() / context.lengthDirection;
context.minViolation = Math.min(context.minViolation, distance);
cancel = true;

View File

@ -18,14 +18,13 @@ import org.bukkit.util.Vector;
/**
* Context data for the direction check, for repeated use within a loop.
* @author mc_dev
*
* @author asofold
*
*/
public class DirectionContext {
public boolean damagedComplex;
public double damagedWidth;
public double damagedHeight;
public Vector direction = null;
public double lengthDirection;

View File

@ -200,7 +200,7 @@ public class FightListener extends CheckListener implements JoinLeaveListener{
// 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);
damagedTrace = MovingData.getData(damagedPlayer).updateTrace(damagedPlayer, damagedLoc, tick, damagedIsFake ? null : mcAccess.getHandle());
}
else {
damagedPlayer = null; // TODO: This is a temporary workaround.
@ -410,9 +410,8 @@ public class FightListener extends CheckListener implements JoinLeaveListener{
boolean cancelled = false;
// (Might pass generic context to factories, for shared + heavy properties.)
final SharedContext sharedContext = new SharedContext(damaged, damagedIsFake, mcAccess.getHandle());
final ReachContext reachContext = reachEnabled ? reach.getContext(player, loc, damaged, damagedLoc, data, cc, sharedContext) : null;
final DirectionContext directionContext = directionEnabled ? direction.getContext(player, loc, damaged, damagedIsFake, damagedLoc, data, cc, sharedContext) : null;
final ReachContext reachContext = reachEnabled ? reach.getContext(player, loc, damaged, damagedLoc, data, cc) : null;
final DirectionContext directionContext = directionEnabled ? direction.getContext(player, loc, damaged, damagedIsFake, damagedLoc, data, cc) : null;
final long traceOldest = tick - cc.loopMaxLatencyTicks; // TODO: Set by latency-window.
// TODO: Iterating direction, which, static/dynamic choice.
@ -424,6 +423,7 @@ public class FightListener extends CheckListener implements JoinLeaveListener{
// TODO: Maintain a latency estimate + max diff and invalidate completely (i.e. iterate from latest NEXT time)], or just max latency.
// TODO: Consider a max-distance to "now", for fast invalidation.
long latencyEstimate = -1;
ITraceEntry successEntry = null;
while (traceIt.hasNext()) {
final ITraceEntry entry = traceIt.next();
// Simplistic just check both until end or hit.
@ -450,6 +450,7 @@ public class FightListener extends CheckListener implements JoinLeaveListener{
// TODO: Log/set estimated latency.
violation = false;
latencyEstimate = now - entry.getTime();
successEntry = entry;
break;
}
}
@ -458,7 +459,7 @@ public class FightListener extends CheckListener implements JoinLeaveListener{
// TODO: violation vs. reachPassed + directionPassed (current: fail one = fail all).
if (reachEnabled) {
// TODO: Might ignore if already cancelled by mixed/silent cancel.
if (reach.loopFinish(player, loc, damaged, reachContext, violation, data, cc)) {
if (reach.loopFinish(player, loc, damaged, reachContext, successEntry, violation, data, cc)) {
cancelled = true;
}
}

View File

@ -162,11 +162,10 @@ 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 SharedContext sharedContext) {
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 = sharedContext.damagedHeight;
//context.eyeHeight = player.getEyeHeight();
context.pY = pLoc.getY() + player.getEyeHeight();
return context;
@ -183,7 +182,9 @@ public class Reach extends Check {
* @param cc
* @return
*/
public boolean loopCheck(final Player player, final Location pLoc, final Entity damaged, final ITraceEntry dRef, final ReachContext context, final FightData data, final FightConfig cc) {
public boolean loopCheck(final Player player, final Location pLoc, final Entity damaged,
final ITraceEntry dRef, final ReachContext context,
final FightData data, final FightConfig cc) {
boolean cancel = false;
// Refine y position.
@ -193,8 +194,8 @@ public class Reach extends Check {
if (context.pY <= dY) {
// Keep the foot level y.
}
else if (context.pY >= dY + context.damagedHeight) {
y = dY + context.damagedHeight; // Highest ref y.
else if (context.pY >= dY + dRef.getBoxMarginVertical()) {
y = dY + dRef.getBoxMarginVertical(); // Highest ref y.
}
else {
y = context.pY; // Level with damaged.
@ -230,7 +231,9 @@ public class Reach extends Check {
* @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) {
public boolean loopFinish(final Player player, final Location pLoc, final Entity damaged,
final ReachContext context, final ITraceEntry traceEntry, 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;
@ -281,7 +284,8 @@ public class Reach extends Check {
}
if (data.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));
// TODO: Height: remember successful ITraceEntry
player.sendMessage("NC+: Attack/reach " + damaged.getType()+ (traceEntry == null ? "" : (" height=" + traceEntry.getBoxMarginVertical())) + " dist=" + StringUtil.fdec3.format(lenpRel) +" @" + StringUtil.fdec3.format(data.reachMod));
}
return cancel;

View File

@ -16,16 +16,16 @@ package fr.neatmonster.nocheatplus.checks.fight;
/**
* Context data for the reach check, for repeated use within a loop.
* @author mc_dev
*
* @author asofold
*
*/
public class ReachContext {
public double distanceLimit;
public double distanceMin;
public double damagedHeight;
// /** Attacking player. */
// public double eyeHeight;
// /** Attacking player. */
// public double eyeHeight;
/** Eye location y of the attacking player. */
public double pY;
/** Minimum value of lenpRel that was a violation. */

View File

@ -1,34 +0,0 @@
/*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package fr.neatmonster.nocheatplus.checks.fight;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import fr.neatmonster.nocheatplus.compat.MCAccess;
public class SharedContext {
public final double damagedHeight;
public SharedContext(Entity damaged, boolean damagedIsFake, MCAccess mcAccess) {
if (damagedIsFake) {
// Assume something lenient then.
damagedHeight = damaged instanceof LivingEntity ? ((LivingEntity) damaged).getEyeHeight() : 1.75;
}
else {
this.damagedHeight = mcAccess.getHeight(damaged);
}
}
}

View File

@ -43,6 +43,7 @@ import fr.neatmonster.nocheatplus.checks.moving.velocity.FrictionAxisVelocity;
import fr.neatmonster.nocheatplus.checks.moving.velocity.SimpleAxisVelocity;
import fr.neatmonster.nocheatplus.checks.moving.velocity.SimpleEntry;
import fr.neatmonster.nocheatplus.checks.workaround.WRPT;
import fr.neatmonster.nocheatplus.compat.MCAccess;
import fr.neatmonster.nocheatplus.compat.blocks.BlockChangeTracker.BlockChangeEntry;
import fr.neatmonster.nocheatplus.compat.blocks.BlockChangeTracker.BlockChangeReference;
import fr.neatmonster.nocheatplus.components.data.ICanHandleTimeRunningBackwards;
@ -1062,36 +1063,39 @@ public class MovingData extends ACheckData {
}
/**
* Convenience method to add a location to the trace, creates the trace if necessary.
* Convenience method to add a location to the trace, creates the trace if
* necessary.
*
* @param player
* @param loc
* @param time
* @return Updated LocationTrace instance, for convenient use, without sticking too much to MovingData.
* @param mcAccess
* If null getEyeHeight and 0.3 are used (assume fake player).
* @return Updated LocationTrace instance, for convenient use, without
* sticking too much to MovingData.
*/
public LocationTrace updateTrace(final Player player, final Location loc, final long time) {
public LocationTrace updateTrace(final Player player, final Location loc, final long time,
final MCAccess mcAccess) {
final LocationTrace trace = getTrace(player);
trace.addEntry(time, loc.getX(), loc.getY(), loc.getZ());
if (mcAccess == null) {
// TODO: 0.3 from bukkit based default heights (needs extra registered classes).
trace.addEntry(time, loc.getX(), loc.getY(), loc.getZ(), 0.3, player.getEyeHeight());
}
else {
trace.addEntry(time, loc.getX(), loc.getY(), loc.getZ(), mcAccess.getWidth(player) / 2.0, Math.max(player.getEyeHeight(), mcAccess.getHeight(player)));
}
return trace;
}
/**
* Convenience
* @param player
* @param loc
*/
public void resetTrace(final Player player, final Location loc, final long time) {
final MovingConfig cc = MovingConfig.getConfig(player);
resetTrace(loc, time, cc);
}
/**
* Convenience.
* @param loc
* @param time
* @param cc
*/
public void resetTrace(final Location loc, final long time, final MovingConfig cc) {
resetTrace(loc, time, cc.traceMaxAge, cc.traceMaxSize);
public void resetTrace(final Player player, final Location loc, final long time,
final MCAccess mcAccess, final MovingConfig cc) {
resetTrace(player, loc, time, cc.traceMaxAge, cc.traceMaxSize, mcAccess);
}
/**
@ -1101,11 +1105,13 @@ public class MovingData extends ACheckData {
* @param mergeDist
* @param traceMergeDist
*/
public void resetTrace(final Location loc, final long time, final int maxAge, final int maxSize) {
public void resetTrace(final Player player, final Location loc, final long time,
final int maxAge, final int maxSize, final MCAccess mcAccess) {
if (trace != null) {
trace.reset();
}
getTrace(maxAge, maxSize).addEntry(time, loc.getX(), loc.getY(), loc.getZ());
getTrace(maxAge, maxSize).addEntry(time, loc.getX(), loc.getY(), loc.getZ(),
mcAccess.getWidth(player) / 2.0, Math.max(player.getEyeHeight(), mcAccess.getHeight(player)));
}
/**

View File

@ -313,7 +313,7 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
MovingUtil.ensureChunksLoaded(player, loc, "world change", data, cc);
}
aux.resetPositionsAndMediumProperties(player, loc, data, cc);
data.resetTrace(loc, TickTask.getTick(), cc);
data.resetTrace(player, loc, TickTask.getTick(), mcAccess.getHandle(), cc);
if (cc.enforceLocation) {
// Just in case.
playersEnforce.add(player.getName());
@ -1109,7 +1109,7 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
// TODO: teleported + other resetting ?
Combined.feedYawRate(player, from.getYaw(), now, from.getWorld().getName(), data);
aux.resetPositionsAndMediumProperties(player, from, mData, mCc);
mData.resetTrace(player, from, tick); // TODO: Should probably leave this to the teleport event!
mData.resetTrace(player, from, tick, mcAccess.getHandle(), mCc); // TODO: Should probably leave this to the teleport event!
if (((NetConfig) CheckType.NET_FLYINGFREQUENCY.getConfigFactory().getConfig(player)).flyingFrequencyActive) {
((NetData) CheckType.NET_FLYINGFREQUENCY.getDataFactory().getData(player)).teleportQueue.onTeleportEvent(from.getX(), from.getY(), from.getZ(), from.getYaw(), from.getPitch());
}
@ -1135,12 +1135,12 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
final Location ref = player.getVehicle().getLocation(useLoc);
aux.resetPositionsAndMediumProperties(player, ref, mData, mCc); // TODO: Consider using to and intercept cheat attempts in another way.
useLoc.setWorld(null);
mData.updateTrace(player, to, tick); // TODO: Can you become invincible by sending special moves?
mData.updateTrace(player, to, tick, mcAccess.getHandle()); // TODO: Can you become invincible by sending special moves?
}
else if (!from.getWorld().getName().equals(toWorldName)) {
// A teleport event should follow.
aux.resetPositionsAndMediumProperties(player, to, mData, mCc);
mData.resetTrace(player, to, tick);
mData.resetTrace(player, to, tick, mcAccess.getHandle(), mCc);
}
else {
// TODO: Detect differing location (a teleport event would follow).
@ -1152,7 +1152,7 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
else {
// Normal move, nothing to do.
}
mData.updateTrace(player, to, tick);
mData.updateTrace(player, to, tick, mcAccess.getHandle());
}
}
@ -1610,7 +1610,7 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
data.clearVehicleData(); // TODO: Uncertain here, what to check.
data.clearAllMorePacketsData();
data.removeAllVelocity();
data.resetTrace(loc, tick, cc); // Might reset to loc instead of set-back ?
data.resetTrace(player, loc, tick, mcAccess.getHandle(), cc); // Might reset to loc instead of set-back ?
// More resetting.
data.vDistAcc.clear();

View File

@ -18,6 +18,7 @@ import java.util.Iterator;
import fr.neatmonster.nocheatplus.components.location.IGetPosition;
import fr.neatmonster.nocheatplus.components.pool.AbstractPool;
import fr.neatmonster.nocheatplus.utilities.RichBoundsLocation;
/**
* This class is meant to record locations for players moving, in order to allow
@ -45,18 +46,22 @@ public class LocationTrace {
public static interface ITraceEntry extends IGetPosition {
public double getBoxMarginHorizontal();
public double getBoxMarginVertical();
public long getTime();
}
public static class TraceEntry implements ITraceEntry {
// TODO: Consider using a simple base implementation for IGetSetPosiotion.
/** We keep it open, if ticks or ms are used. */
private long time;
/** Coordinates. */
private double x, y, z;
private double x, y, z, boxMarginHorizontal, boxMarginVertical;
/** Older/next. */
private TraceEntry next;
@ -71,10 +76,13 @@ public class LocationTrace {
* @param y
* @param z
*/
public void set(long time, double x, double y, double z) {
public void set(long time, double x, double y, double z,
double boxMarginHorizontal, double boxMarginVertical) {
this.x = x;
this.y = y;
this.z = z;
this.boxMarginHorizontal = boxMarginHorizontal;
this.boxMarginVertical = boxMarginVertical;
this.time = time;
}
@ -93,6 +101,14 @@ public class LocationTrace {
return z;
}
public double getBoxMarginHorizontal() {
return boxMarginHorizontal;
}
public double getBoxMarginVertical() {
return boxMarginVertical;
}
@Override
public long getTime() {
return time;
@ -192,16 +208,35 @@ public class LocationTrace {
this.maxSize = maxSize;
}
public final void addEntry(final long time, final double x, final double y, final double z) {
public final void addEntry(final long time, final RichBoundsLocation loc) {
addEntry(time, loc.getX(), loc.getY(), loc.getZ(), loc.getBoxMarginHorizontal(), loc.getBoxMarginVertical());
}
/**
*
* @param time
* @param x
* @param y
* @param z
* @param boxMarginHorizontal
* Margin from the foot position to the side(s) of the box.
* @param boxMarginVertical
* Margin from the foot position to the top of the box.
*/
public final void addEntry(final long time, final double x, final double y, final double z,
final double boxMarginHorizontal, final double boxMarginVertical) {
if (size > 0) {
if (x == firstEntry.x && y == firstEntry.y && z == firstEntry.z) {
// TODO: Might update box to bigger or remove margins ?
if (x == firstEntry.x && y == firstEntry.y && z == firstEntry.z
&& boxMarginHorizontal == firstEntry.boxMarginHorizontal
&& boxMarginVertical == firstEntry.boxMarginVertical) {
// (No update of time. firstEntry ... now always counts.)
return;
}
}
// Add a new entry.
final TraceEntry newEntry = pool.getInstance();
newEntry.set(time, x, y, z);
newEntry.set(time, x, y, z, boxMarginHorizontal, boxMarginVertical);
setFirst(newEntry);
// Remove the last entry, if maxSize is exceeded.
if (size > maxSize) {

View File

@ -76,14 +76,14 @@ public class TestLocationTrace {
}
// Adding up to size elements.
for (int i = 0; i < size ; i++) {
trace.addEntry(i, i, i, i);
trace.addEntry(i, i, i, i, 0, 0);
if (trace.size() != i + 1) {
fail("Wrong size, expect " + (i + 1) + ", got instead: " + trace.size());
}
}
// Adding a lot of elements.
for (int i = 0; i < 1000; i ++) {
trace.addEntry(i + size, i, i, i);
trace.addEntry(i + size, i, i, i, 0, 0);
if (trace.size() != size) {
fail("Wrong size, expect " + size + ", got instead: " + trace.size());
}
@ -95,7 +95,7 @@ public class TestLocationTrace {
int size = 80;
LocationTrace trace = new LocationTrace(size, size, pool);
for (int i = 0; i < 1000; i ++) {
trace.addEntry(i + size, 0 , 0, 0);
trace.addEntry(i + size, 0 , 0, 0, 0, 0);
if (trace.size() != 1) {
fail("Wrong size, expect 1, got instead: " + trace.size());
}
@ -133,13 +133,13 @@ public class TestLocationTrace {
LocationTrace trace = new LocationTrace(size, size, pool);
// Adding up to size elements.
for (int i = 0; i < size; i++) {
trace.addEntry(i, i, i, i);
trace.addEntry(i, i, i, i, 0, 0);
}
// Test size with one time filled up.
testIteratorSizeAndOrder(trace, 80);
// Add size / 2 elements, to test cross-boundary iteration.
for (int i = 0; i < size / 2; i++) {
trace.addEntry(i + size, i, i, i);
trace.addEntry(i + size, i, i, i, 0, 0);
}
// Test size again.
testIteratorSizeAndOrder(trace, 80);
@ -195,7 +195,7 @@ public class TestLocationTrace {
LocationTrace trace = new LocationTrace(size, size, pool);
// Adding up to size elements.
for (int i = 0; i < size; i++) {
trace.addEntry(i, i, i, i);
trace.addEntry(i, i, i, i, 0, 0);
}
for (int i = 0; i < size; i++) {
Iterator<ITraceEntry> it = trace.maxAgeIterator(i);
@ -218,11 +218,11 @@ public class TestLocationTrace {
public void testMaxAgeFirstElementAnyway() {
int size = 80;
LocationTrace trace = new LocationTrace(size, size, pool);
trace.addEntry(0, 0, 0, 0);
trace.addEntry(0, 0, 0, 0, 0, 0);
if (!trace.maxAgeIterator(1000).hasNext()) {
fail("Expect iterator (maxAge) to always contain the latest element.");
}
trace.addEntry(1, 0, 0, 0);
trace.addEntry(1, 0, 0, 0, 0, 0);
final Iterator<ITraceEntry> it = trace.maxAgeIterator(2);
if (!it.hasNext()) {
fail("Expect iterator (maxAge) to always contain the latest element.");