From f0c1f34438677a3871fbce7beca12422d28f5729 Mon Sep 17 00:00:00 2001 From: asofold Date: Wed, 9 Jan 2013 20:44:54 +0100 Subject: [PATCH] [Bleeding] Holzhammer 2. MovingData: Set set backs and teleported to private visibility in order to be able to rule out potential sources of inconsistencies. Also set morepackets set-backs to null rather than to the same reference of set-back (onSetBack). --- .../checks/moving/CreativeFly.java | 6 +- .../nocheatplus/checks/moving/LocUtil.java | 74 ++++++++++ .../checks/moving/MorePackets.java | 16 ++- .../checks/moving/MorePacketsVehicle.java | 11 +- .../nocheatplus/checks/moving/MovingData.java | 131 +++++++++++++----- .../checks/moving/MovingListener.java | 42 +++--- .../nocheatplus/checks/moving/Passable.java | 7 +- .../checks/moving/SurvivalFly.java | 12 +- 8 files changed, 223 insertions(+), 76 deletions(-) create mode 100644 NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/checks/moving/LocUtil.java diff --git a/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/checks/moving/CreativeFly.java b/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/checks/moving/CreativeFly.java index df1ea78e..a54fb51d 100644 --- a/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/checks/moving/CreativeFly.java +++ b/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/checks/moving/CreativeFly.java @@ -54,13 +54,13 @@ public class CreativeFly extends Check { public Location check(final Player player, final PlayerLocation from, final PlayerLocation to, final MovingData data, final MovingConfig cc) { // If we have no setback, define one now. - if (data.setBack == null) - data.setBack = from.getLocation(); + if (!data.hasSetBack()) + data.setSetBack(from); // Before doing anything, do a basic height check to determine if players are flying too high. final int maximumHeight = cc.creativeFlyMaxHeight + player.getWorld().getMaxHeight(); if (to.getY() - data.verticalFreedom > maximumHeight) - return new Location(player.getWorld(), data.setBack.getX(), maximumHeight - 10D, data.setBack.getZ(), + return new Location(player.getWorld(), data.getSetBackX(), maximumHeight - 10D, data.getSetBackZ(), to.getYaw(), to.getPitch()); // Calculate some distances. diff --git a/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/checks/moving/LocUtil.java b/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/checks/moving/LocUtil.java new file mode 100644 index 00000000..e5c6cbd9 --- /dev/null +++ b/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/checks/moving/LocUtil.java @@ -0,0 +1,74 @@ +package fr.neatmonster.nocheatplus.checks.moving; + +import org.bukkit.Location; + +import fr.neatmonster.nocheatplus.utilities.PlayerLocation; + +public class LocUtil { + + /** + * Simple get a copy (not actually using cloning). + * @param loc + * @return + */ + static final Location clone(final Location loc){ + return new Location(loc.getWorld(), loc.getX(), loc.getY(), loc.getZ(), loc.getYaw(), loc.getPitch()); + } + + /** + * Clone with given yaw and pitch. + * @param loc + * @param yaw + * @param pitch + * @return + */ + static final Location clone(final Location loc, final float yaw, final float pitch){ + return new Location(loc.getWorld(), loc.getX(), loc.getY(), loc.getZ(), yaw, pitch); + } + + /** + * Clone with yaw and pitch of ref, use ref if setBack is null. + * @param setBack + * @param ref + * @return + */ + static final Location clone(final Location setBack, final Location ref) { + if (setBack == null){ + return clone(ref); + } + else{ + return clone(setBack, ref.getYaw(), ref.getPitch()); + } + } + + static final Location clone(final Location setBack, final PlayerLocation ref) { + if (setBack == null) return ref.getLocation(); + else{ + return clone(setBack, ref.getYaw(), ref.getPitch()); + } + } + + /** + * SA + * @param setBack + * @param loc + */ + static final void set(final Location setBack, final Location loc) { + setBack.setWorld(loc.getWorld()); + setBack.setX(loc.getX()); + setBack.setY(loc.getY()); + setBack.setZ(loc.getZ()); + setBack.setYaw(loc.getYaw()); + setBack.setPitch(loc.getPitch()); + } + + static final void set(final Location setBack, final PlayerLocation loc) { + setBack.setWorld(loc.getWorld()); + setBack.setX(loc.getX()); + setBack.setY(loc.getY()); + setBack.setZ(loc.getZ()); + setBack.setYaw(loc.getYaw()); + setBack.setPitch(loc.getPitch()); + } + +} diff --git a/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MorePackets.java b/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MorePackets.java index 38cbb92b..035c9193 100644 --- a/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MorePackets.java +++ b/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MorePackets.java @@ -67,10 +67,10 @@ public class MorePackets extends Check { Location newTo = null; - if (data.morePacketsSetback == null){ + if (!data.hasMorePacketsSetBack()){ // TODO: Check if other set-back is appropriate or if to set on other events. - if (data.setBack != null) data.morePacketsSetback = data.getSetBack(to); - else data.morePacketsSetback = from.getLocation(); + if (data.hasSetBack()) data.setMorePacketsSetBack(data.getSetBack(to)); + else data.setMorePacketsSetBack(from); } // Take a packet from the buffer. @@ -86,8 +86,11 @@ public class MorePackets extends Check { // Execute whatever actions are associated with this check and the violation level and find out if we should // cancel the event. if (executeActions(player, data.morePacketsVL, -data.morePacketsBuffer, - MovingConfig.getConfig(player).morePacketsActions)) - newTo = data.teleported = data.morePacketsSetback; + MovingConfig.getConfig(player).morePacketsActions)){ + newTo = data.getMorePacketsSetBack(); + data.setTeleported(newTo); + } + } if (data.morePacketsLastTime + 1000 < time) { @@ -109,8 +112,7 @@ public class MorePackets extends Check { data.morePacketsLastTime = time; // Set the new "setback" location. - if (newTo == null) - data.morePacketsSetback = from.getLocation(); + if (newTo == null) data.setMorePacketsSetBack(from); } else if (data.morePacketsLastTime > time) // Security check, maybe system time changed. data.morePacketsLastTime = time; diff --git a/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MorePacketsVehicle.java b/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MorePacketsVehicle.java index 63edc42f..5e6adb0a 100644 --- a/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MorePacketsVehicle.java +++ b/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MorePacketsVehicle.java @@ -67,9 +67,9 @@ public class MorePacketsVehicle extends Check { Location newTo = null; - if (data.morePacketsVehicleSetback == null){ + if (!data.hasMorePacketsVehicleSetBack()){ // TODO: Check if other set-back is appropriate or if to set on other events. - data.morePacketsVehicleSetback = from; + data.setMorePacketsVehicleSetBack(from); } // Take a packet from the buffer. @@ -85,8 +85,9 @@ public class MorePacketsVehicle extends Check { // Execute whatever actions are associated with this check and the violation level and find out if we should // cancel the event. if (executeActions(player, data.morePacketsVehicleVL, -data.morePacketsVehicleBuffer, - MovingConfig.getConfig(player).morePacketsVehicleActions)) - newTo = data.morePacketsVehicleSetback; + MovingConfig.getConfig(player).morePacketsVehicleActions)){ + newTo = data.getMorePacketsVehicleSetBack(); + } } if (data.morePacketsVehicleLastTime + 1000 < time) { @@ -109,7 +110,7 @@ public class MorePacketsVehicle extends Check { // Set the new "setback" location. if (newTo == null) - data.morePacketsVehicleSetback = from; + data.setMorePacketsVehicleSetBack(from); } else if (data.morePacketsVehicleLastTime > time) // Security check, maybe system time changed. data.morePacketsVehicleLastTime = time; diff --git a/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingData.java b/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingData.java index 9a905e14..994f89e5 100644 --- a/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingData.java +++ b/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingData.java @@ -68,8 +68,8 @@ public class MovingData extends ACheckData { public static void clear(){ playersMap.clear(); } - - // Violation levels. + + // Violation levels. public double creativeFlyVL = 0D; public double morePacketsVL = 0D; public double morePacketsVehicleVL = 0D; @@ -98,13 +98,13 @@ public class MovingData extends ACheckData { public int morePacketsBuffer = 50; public long morePacketsLastTime; public int morePacketsPackets; - public Location morePacketsSetback; + private Location morePacketsSetback; // Data of the more packets vehicle check. public int morePacketsVehicleBuffer = 50; public long morePacketsVehicleLastTime; public int morePacketsVehiclePackets; - public Location morePacketsVehicleSetback; + private Location morePacketsVehicleSetback; // Data of the no fall check. public float noFallFallDistance; @@ -140,8 +140,8 @@ public class MovingData extends ACheckData { public final ActionFrequency vDistCount = new ActionFrequency(3, 333); // Locations shared between all checks. - public Location setBack; - public Location teleported; + private Location setBack; + private Location teleported; /** * Clear the data of the fly checks (not more-packets). @@ -168,7 +168,10 @@ public class MovingData extends ACheckData { // Reset positions resetPositions(teleported); // NOTE: Do mind that the reference is used directly for set-backs, should stay consistent, though. - this.setBack = this.morePacketsSetback = this.morePacketsVehicleSetback = teleported; + + setSetBack(teleported); + this.morePacketsSetback = this.morePacketsVehicleSetback = null; // TODO: or set. + clearAccounting(); // Might be more safe to do this. // Keep no-fall data. // Fly data: problem is we don't remember the settings for the set back location. @@ -241,12 +244,7 @@ public class MovingData extends ACheckData { setBack = loc.getLocation(); } else{ - setBack.setWorld(loc.getWorld()); - setBack.setX(loc.getX()); - setBack.setY(loc.getY()); - setBack.setZ(loc.getZ()); - setBack.setYaw(loc.getYaw()); - setBack.setPitch(loc.getPitch()); + LocUtil.set(setBack, loc); } } @@ -256,41 +254,108 @@ public class MovingData extends ACheckData { */ public void setSetBack(final Location loc){ if (setBack == null){ - setBack = new Location(loc.getWorld(), loc.getX(), loc.getY(), loc.getZ(), loc.getYaw(), loc.getPitch()); + setBack = LocUtil.clone(loc); } else{ - setBack.setWorld(loc.getWorld()); - setBack.setX(loc.getX()); - setBack.setY(loc.getY()); - setBack.setZ(loc.getZ()); - setBack.setYaw(loc.getYaw()); - setBack.setPitch(loc.getPitch()); + LocUtil.set(setBack, loc); } } - - /** + + /** * Get the set-back location with yaw and pitch set form ref. * @param ref * @return */ public Location getSetBack(final Location ref){ - if (setBack == null){ - return new Location(ref.getWorld(), ref.getX(), ref.getY(), ref.getZ(), ref.getYaw(), ref.getPitch()); - } - else{ - return new Location(setBack.getWorld(), setBack.getX(), setBack.getY(), setBack.getZ(), ref.getYaw(), ref.getPitch()); - } + return LocUtil.clone(setBack, ref); } - /** + /** * Get the set-back location with yaw and pitch set from ref. * @param ref * @return */ public Location getSetBack(final PlayerLocation ref) { - if (setBack == null) return ref.getLocation(); - else{ - return new Location(setBack.getWorld(), setBack.getX(), setBack.getY(), setBack.getZ(), ref.getYaw(), ref.getPitch()); - } + return LocUtil.clone(setBack, ref); } + + public boolean hasSetBack() { + return setBack != null; + } + + public boolean hasSetBackWorldChanged(final Location loc) { + if (setBack == null) return true; + else return setBack.getWorld().equals(loc.getWorld()); + } + + + public double getSetBackX() { + return setBack.getX(); + } + + public double getSetBackY() { + return setBack.getY(); + } + + public double getSetBackZ() { + return setBack.getZ(); + } + + public void setSetBackY(final double y) { + setBack.setY(y); + } + + public final Location getTeleported(){ + // TODO: here a reference might do. + return teleported == null ? teleported : LocUtil.clone(teleported); + } + + public final void setTeleported(final Location loc) { + teleported = LocUtil.clone(loc); // Always overwrite. + } + + public boolean hasMorePacketsSetBack() { + return morePacketsSetback != null; + } + + public final void setMorePacketsSetBack(final PlayerLocation loc) { + if (morePacketsSetback == null) morePacketsSetback = loc.getLocation(); + else LocUtil.set(morePacketsSetback, loc); + } + + public final void setMorePacketsSetBack(final Location loc) { + if (morePacketsSetback == null) morePacketsSetback = LocUtil.clone(loc); + else LocUtil.set(morePacketsSetback, loc); + } + + public Location getMorePacketsSetBack() { + return LocUtil.clone(morePacketsSetback); + } + + public boolean hasMorePacketsVehicleSetBack() { + return morePacketsVehicleSetback != null; + } + + public final void setMorePacketsVehicleSetBack(final PlayerLocation loc) { + if (morePacketsVehicleSetback == null) morePacketsVehicleSetback = loc.getLocation(); + else LocUtil.set(morePacketsVehicleSetback, loc); + } + + public final void setMorePacketsVehicleSetBack(final Location loc) { + if (morePacketsVehicleSetback == null) morePacketsVehicleSetback = LocUtil.clone(loc); + else LocUtil.set(morePacketsVehicleSetback, loc); + } + + public final Location getMorePacketsVehicleSetBack() { + return LocUtil.clone(morePacketsVehicleSetback); + } + + public final void resetTeleported() { + teleported = null; + } + + public final void resetSetBack() { + setBack = null; + } + } diff --git a/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingListener.java b/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingListener.java index 202ff1fd..dc87aaa7 100644 --- a/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingListener.java +++ b/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/checks/moving/MovingListener.java @@ -178,7 +178,7 @@ public class MovingListener extends CheckListener{ final MovingData data = MovingData.getData(player); if (!creativeFly.isEnabled(player) && !survivalFly.isEnabled(player)) return; - if (data.setBack == null || blockY + 1D < data.setBack.getY()) return; + if (!data.hasSetBack() || blockY + 1D < data.getSetBackY()) return; final Location loc = player.getLocation(); if (Math.abs(loc.getX() - 0.5 - block.getX()) <= 1D @@ -188,7 +188,7 @@ public class MovingListener extends CheckListener{ // The creative fly and/or survival fly check is enabled, the // block was placed below the player and is // solid, so do what we have to do. - data.setBack.setY(blockY + 1D); + data.setSetBackY(blockY + 1D); data.sfJumpPhase = 0; } } @@ -250,11 +250,11 @@ public class MovingListener extends CheckListener{ if (sfCheck && noFall.isEnabled(player)){ // Check if to deal damage. double y = loc.getY(); - if (data.setBack != null) y = Math.min(y, data.setBack.getY()); + if (data.hasSetBack()) y = Math.min(y, data.getSetBackY()); noFall.checkDamage(player, data, y); } // Teleport. - data.teleported = target; // Should be enough. | new Location(target.getWorld(), target.getX(), target.getY(), target.getZ(), target.getYaw(), target.getPitch()); + data.setTeleported(target); // Should be enough. | new Location(target.getWorld(), target.getX(), target.getY(), target.getZ(), target.getYaw(), target.getPitch()); player.teleport(target, TeleportCause.PLUGIN);// TODO: schedule / other measures ? } } @@ -285,11 +285,10 @@ public class MovingListener extends CheckListener{ // Maybe this helps with people teleporting through Multiverse portals having problems? final Player player = event.getPlayer(); final MovingData data = MovingData.getData(player); - data.teleported = null; data.clearFlyData(); data.clearMorePacketsData(); // TODO: Might omit this if neither check is activated. - data.setBack = player.getLocation(); + data.setSetBack(player.getLocation()); } /** @@ -386,7 +385,7 @@ public class MovingListener extends CheckListener{ final MovingData data = MovingData.getData(player); data.noFallAssumeGround = false; - data.teleported = null; + data.resetTeleported(); // Check for illegal move and bounding box etc. if (pFrom.isIllegal() || pTo.isIllegal()) { @@ -479,7 +478,7 @@ public class MovingListener extends CheckListener{ event.setTo(newTo); // Remember where we send the player to. - data.teleported = newTo; + data.setTeleported(newTo); if (cc.debug){ System.out.println(player.getName() + " set back to: " + newTo.getWorld() + CheckUtils.fdec3.format(newTo.getX()) + ", " + CheckUtils.fdec3.format(newTo.getY()) + ", " + CheckUtils.fdec3.format(newTo.getZ())); } @@ -506,17 +505,18 @@ public class MovingListener extends CheckListener{ boolean restored = false; final PlayerLocation pLoc = new PlayerLocation(NoCheatPlus.getMCAccess(), null); // (Mind that we don't set the block cache here). - if (!restored && data.setBack != null) { - pLoc.set(data.setBack, player); + final Location loc = player.getLocation(); + if (!restored && data.hasSetBack()) { + final Location setBack = data.getSetBack(loc); + pLoc.set(setBack, player); if (!pLoc.isIllegal()){ - event.setFrom(data.setBack); - event.setTo(data.setBack); + event.setFrom(setBack); + event.setTo(setBack); restored = true; } - else data.setBack = null; + else data.resetSetBack(); } if (!restored){ - final Location loc = player.getLocation(); pLoc.set(loc, player); if (!pLoc.isIllegal()) { event.setFrom(loc); @@ -635,7 +635,7 @@ public class MovingListener extends CheckListener{ final MovingData data = MovingData.getData(player); data.clearFlyData(); data.clearMorePacketsData(); - data.setBack = event.getRespawnLocation(); + data.setSetBack(event.getRespawnLocation()); // TODO: consider data.resetPositions(data.setBack); } @@ -649,7 +649,7 @@ public class MovingListener extends CheckListener{ final MovingData data = MovingData.getData(player); data.clearFlyData(); data.clearMorePacketsData(); - data.setBack = player.getLocation(); // TODO: Monitor this change (!). + data.setSetBack(player.getLocation()); // TODO: Monitor this change (!). } /** @@ -674,7 +674,7 @@ public class MovingListener extends CheckListener{ final Player player = event.getPlayer(); final MovingData data = MovingData.getData(player); - final Location teleported = data.teleported; + final Location teleported = data.getTeleported(); // If it was a teleport initialized by NoCheatPlus, do it anyway even if another plugin said "no". final Location to = event.getTo(); @@ -705,7 +705,7 @@ public class MovingListener extends CheckListener{ } // TODO: NoFall might be necessary to be checked here ? - data.teleported = null; + data.resetTeleported(); // Reset yawrate (experimental: might help preventing cascading improbable with rubberbanding). Combined.resetYawRate(player, ref.getYaw(), System.currentTimeMillis(), true); @@ -835,7 +835,7 @@ public class MovingListener extends CheckListener{ if (!pLoc.isOnGround() && !pLoc.isResetCond() && !pLoc.isAboveLadder() && !pLoc.isAboveStairs()){ // Likely a new style no-fall bypass (damage in mid-air). data.noFallVL += 1.0; - if (noFall.executeActions(player, data.noFallVL, 1.0, cc.noFallActions, true) && data.setBack != null){ + if (noFall.executeActions(player, data.noFallVL, 1.0, cc.noFallActions, true) && data.hasSetBack()){ // Cancel the event and restore fall distance. // NoFall data will not be reset allowReset = false; @@ -866,8 +866,8 @@ public class MovingListener extends CheckListener{ // TODO: on existing set back: detect world changes and loss of world on join (+ set up some paradigm). data.clearMorePacketsData(); final Location loc = player.getLocation(); - if (data.setBack == null){ - data.setBack = loc; + if (!data.hasSetBack() || data.hasSetBackWorldChanged(loc)){ + data.setSetBack(loc); } if (data.fromX == Double.MAX_VALUE && data.toX == Double.MAX_VALUE){ // TODO: re-think: more fine grained reset? diff --git a/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/checks/moving/Passable.java b/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/checks/moving/Passable.java index 610f5471..59bc952f 100644 --- a/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/checks/moving/Passable.java +++ b/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/checks/moving/Passable.java @@ -61,7 +61,12 @@ public class Passable extends Check { } // Prefer the set-back location from the data. - if (data.setBack != null && BlockProperties.isPassable(from.getBlockCache(), data.setBack)) loc = data.getSetBack(to); + if (data.hasSetBack()){ + final Location ref = data.getSetBack(to); + if (BlockProperties.isPassable(from.getBlockCache(), ref)){ + loc = ref; + } + } // TODO: set data.set-back ? or something: still some aji here. diff --git a/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/checks/moving/SurvivalFly.java b/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/checks/moving/SurvivalFly.java index 4154a04f..bda36f15 100644 --- a/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/checks/moving/SurvivalFly.java +++ b/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/checks/moving/SurvivalFly.java @@ -97,8 +97,8 @@ public class SurvivalFly extends Check { final double hDistance = Math.sqrt(xDistance * xDistance + zDistance * zDistance); // If we don't have any setBack, choose the location the player comes from. - if (data.setBack == null) - data.setBack = from.getLocation(); + if (!data.hasSetBack()) + data.setSetBack(from); final boolean resetFrom; @@ -278,7 +278,7 @@ public class SurvivalFly extends Check { } // TODO: This might need max(0, for ydiff) - vDistanceAboveLimit = to.getY() - data.setBack.getY() - vAllowedDistance; + vDistanceAboveLimit = to.getY() - data.getSetBackY() - vAllowedDistance; if (vDistanceAboveLimit > 0) tags.add("vdist"); @@ -364,7 +364,7 @@ public class SurvivalFly extends Check { // Check for "lost touch", for when moving events were not created, // for instance (1/256). if (!useWorkaround && data.fromX != Double.MAX_VALUE && yDistance > 0 && yDistance < 0.5 && data.sfLastYDist < 0) { - final double setBackYDistance = to.getY() - data.setBack.getY(); + final double setBackYDistance = to.getY() - data.getSetBackY(); if (setBackYDistance > 0D && setBackYDistance <= 1.5D) { // Interpolate from last to-coordinates to the from // coordinates (with some safe-guard). @@ -392,7 +392,7 @@ public class SurvivalFly extends Check { else{ // TODO: This seems dubious ! // Consider: 1.0 + ? or max(from.getY(), 1.0 + ...) ? - data.setBack.setY(Location.locToBlock(data.setBack.getY())); + data.setSetBackY(Location.locToBlock(data.getSetBackY())); } // data.ground ? // ? set jumpphase to height / 0.15 ? @@ -622,7 +622,7 @@ public class SurvivalFly extends Check { } else data.sfCobwebVL += vDistanceAboveLimit * 100D; if (data.sfCobwebVL < 550) { // Totally random ! // Silently set back. - if (data.setBack == null) data.setSetBack(player.getLocation()); + if (!data.hasSetBack()) data.setSetBack(player.getLocation()); // ? check moment of call. data.sfJumpPhase = 0; data.sfLastYDist = Double.MAX_VALUE; return data.getSetBack(to);