summaryrefslogtreecommitdiffstats
path: root/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/moving/MovingCheckListener.java
diff options
context:
space:
mode:
Diffstat (limited to 'EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/moving/MovingCheckListener.java')
-rw-r--r--EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/moving/MovingCheckListener.java376
1 files changed, 376 insertions, 0 deletions
diff --git a/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/moving/MovingCheckListener.java b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/moving/MovingCheckListener.java
new file mode 100644
index 000000000..3b35e0c33
--- /dev/null
+++ b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/moving/MovingCheckListener.java
@@ -0,0 +1,376 @@
+package com.earth2me.essentials.anticheat.checks.moving;
+
+import com.earth2me.essentials.anticheat.EventManager;
+import com.earth2me.essentials.anticheat.NoCheat;
+import com.earth2me.essentials.anticheat.NoCheatPlayer;
+import com.earth2me.essentials.anticheat.checks.CheckUtil;
+import com.earth2me.essentials.anticheat.config.ConfigurationCacheStore;
+import com.earth2me.essentials.anticheat.config.Permissions;
+import com.earth2me.essentials.anticheat.data.PreciseLocation;
+import java.util.LinkedList;
+import java.util.List;
+import org.bukkit.Location;
+import org.bukkit.block.Block;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.Listener;
+import org.bukkit.event.block.BlockPlaceEvent;
+import org.bukkit.event.player.*;
+import org.bukkit.util.Vector;
+
+
+/**
+ * Central location to listen to events that are relevant for the moving checks
+ *
+ */
+public class MovingCheckListener implements Listener, EventManager
+{
+ private final MorePacketsCheck morePacketsCheck;
+ private final FlyingCheck flyingCheck;
+ private final RunningCheck runningCheck;
+ private final NoCheat plugin;
+
+ public MovingCheckListener(NoCheat plugin)
+ {
+
+ flyingCheck = new FlyingCheck(plugin);
+ runningCheck = new RunningCheck(plugin);
+ morePacketsCheck = new MorePacketsCheck(plugin);
+
+ this.plugin = plugin;
+ }
+
+ /**
+ * A workaround for players placing blocks below them getting pushed off the block by NoCheat.
+ *
+ * It essentially moves the "setbackpoint" to the top of the newly placed block, therefore tricking NoCheat into
+ * thinking the player was already on top of that block and should be allowed to stay there
+ *
+ * @param event The BlockPlaceEvent
+ */
+ @EventHandler(priority = EventPriority.MONITOR)
+ public void blockPlace(final BlockPlaceEvent event)
+ {
+
+ // Block wasn't placed, so we don't care
+ if (event.isCancelled())
+ {
+ return;
+ }
+
+ final NoCheatPlayer player = plugin.getPlayer(event.getPlayer());
+ final MovingConfig config = MovingCheck.getConfig(player);
+
+ // If the player is allowed to fly anyway, the workaround is not needed
+ // It's kind of expensive (looking up block types) therefore it makes
+ // sense to avoid it
+ if (config.allowFlying || !config.runflyCheck || player.hasPermission(Permissions.MOVING_FLYING) || player.hasPermission(Permissions.MOVING_RUNFLY))
+ {
+ return;
+ }
+
+ // Get the player-specific stored data that applies here
+ final MovingData data = MovingCheck.getData(player);
+
+ final Block block = event.getBlockPlaced();
+
+ if (block == null || !data.runflySetBackPoint.isSet())
+ {
+ return;
+ }
+
+ // Keep some results of "expensive calls
+ final Location l = player.getPlayer().getLocation();
+ final int playerX = l.getBlockX();
+ final int playerY = l.getBlockY();
+ final int playerZ = l.getBlockZ();
+ final int blockY = block.getY();
+
+ // Was the block below the player?
+ if (Math.abs(playerX - block.getX()) <= 1 && Math.abs(playerZ - block.getZ()) <= 1 && playerY - blockY >= 0 && playerY - blockY <= 2)
+ {
+ // yes
+ final int type = CheckUtil.getType(block.getTypeId());
+ if (CheckUtil.isSolid(type) || CheckUtil.isLiquid(type))
+ {
+ if (blockY + 1 >= data.runflySetBackPoint.y)
+ {
+ data.runflySetBackPoint.y = (blockY + 1);
+ data.jumpPhase = 0;
+ }
+ }
+ }
+ }
+
+ /**
+ * If a player gets teleported, it may have two reasons. Either it was NoCheat or another plugin. If it was NoCheat,
+ * the target location should match the "data.teleportTo" value.
+ *
+ * On teleports, reset some movement related data that gets invalid
+ *
+ * @param event The PlayerTeleportEvent
+ */
+ @EventHandler(priority = EventPriority.HIGHEST)
+ public void teleport(final PlayerTeleportEvent event)
+ {
+
+ NoCheatPlayer player = plugin.getPlayer(event.getPlayer());
+ final MovingData data = MovingCheck.getData(player);
+
+ // If it was a teleport initialized by NoCheat, do it anyway
+ // even if another plugin said "no"
+ if (data.teleportTo.isSet() && data.teleportTo.equals(event.getTo()))
+ {
+ event.setCancelled(false);
+ }
+ else
+ {
+ // Only if it wasn't NoCheat, drop data from morepackets check.
+ // If it was NoCheat, we don't want players to exploit the
+ // runfly check teleporting to get rid of the "morepackets"
+ // data.
+ data.clearMorePacketsData();
+ }
+
+ // Always drop data from runfly check, as it always loses its validity
+ // after teleports. Always!
+ data.teleportTo.reset();
+ data.clearRunFlyData();
+
+ return;
+ }
+
+ /**
+ * Just for security, if a player switches between worlds, reset the runfly and morepackets checks data, because it
+ * is definitely invalid now
+ *
+ * @param event The PlayerChangedWorldEvent
+ */
+ @EventHandler(priority = EventPriority.MONITOR)
+ public void worldChange(final PlayerChangedWorldEvent event)
+ {
+ // Maybe this helps with people teleporting through multiverse portals having problems?
+ final MovingData data = MovingCheck.getData(plugin.getPlayer(event.getPlayer()));
+ data.teleportTo.reset();
+ data.clearRunFlyData();
+ data.clearMorePacketsData();
+ }
+
+ /**
+ * When a player uses a portal, all information related to the moving checks becomes invalid.
+ *
+ * @param event
+ */
+ @EventHandler(priority = EventPriority.MONITOR)
+ public void portal(final PlayerPortalEvent event)
+ {
+ final MovingData data = MovingCheck.getData(plugin.getPlayer(event.getPlayer()));
+ data.clearMorePacketsData();
+ data.clearRunFlyData();
+ }
+
+ /**
+ * When a player respawns, all information related to the moving checks becomes invalid.
+ *
+ * @param event
+ */
+ @EventHandler(priority = EventPriority.MONITOR)
+ public void respawn(final PlayerRespawnEvent event)
+ {
+ final MovingData data = MovingCheck.getData(plugin.getPlayer(event.getPlayer()));
+ data.clearMorePacketsData();
+ data.clearRunFlyData();
+ }
+
+ /**
+ * When a player moves, he will be checked for various suspicious behaviour.
+ *
+ * @param event The PlayerMoveEvent
+ */
+ @EventHandler(priority = EventPriority.LOWEST)
+ public void move(final PlayerMoveEvent event)
+ {
+
+ // Don't care for vehicles
+ if (event.isCancelled() || event.getPlayer().isInsideVehicle())
+ {
+ return;
+ }
+
+ // Don't care for movements that are very high distance or to another
+ // world (such that it is very likely the event data was modified by
+ // another plugin before we got it)
+ if (!event.getFrom().getWorld().equals(event.getTo().getWorld()) || event.getFrom().distanceSquared(event.getTo()) > 400)
+ {
+ return;
+ }
+
+ final NoCheatPlayer player = plugin.getPlayer(event.getPlayer());
+
+ final MovingConfig cc = MovingCheck.getConfig(player);
+ final MovingData data = MovingCheck.getData(player);
+
+ // Advance various counters and values that change per movement
+ // tick. They are needed to decide on how fast a player may
+ // move.
+ tickVelocities(data);
+
+ // Remember locations
+ data.from.set(event.getFrom());
+ final Location to = event.getTo();
+ data.to.set(to);
+
+ PreciseLocation newTo = null;
+
+ /**
+ * RUNFLY CHECK SECTION *
+ */
+ // If the player isn't handled by runfly checks
+ if (!cc.runflyCheck || player.hasPermission(Permissions.MOVING_RUNFLY))
+ {
+ // Just because he is allowed now, doesn't mean he will always
+ // be. So forget data about the player related to moving
+ data.clearRunFlyData();
+ }
+ else if (cc.allowFlying || (player.isCreative() && cc.identifyCreativeMode) || player.hasPermission(Permissions.MOVING_FLYING))
+ {
+ // Only do the limited flying check
+ newTo = flyingCheck.check(player, data, cc);
+ }
+ else
+ {
+ // Go for the full treatment
+ newTo = runningCheck.check(player, data, cc);
+ }
+
+ /**
+ * MOREPACKETS CHECK SECTION *
+ */
+ if (!cc.morePacketsCheck || player.hasPermission(Permissions.MOVING_MOREPACKETS))
+ {
+ data.clearMorePacketsData();
+ }
+ else if (newTo == null)
+ {
+ newTo = morePacketsCheck.check(player, data, cc);
+ }
+
+ // Did one of the check(s) decide we need a new "to"-location?
+ if (newTo != null)
+ {
+ // Compose a new location based on coordinates of "newTo" and
+ // viewing direction of "event.getTo()" to allow the player to
+ // look somewhere else despite getting pulled back by NoCheat
+ event.setTo(new Location(player.getPlayer().getWorld(), newTo.x, newTo.y, newTo.z, to.getYaw(), to.getPitch()));
+
+ // remember where we send the player to
+ data.teleportTo.set(newTo);
+ }
+ }
+
+ /**
+ * Just try to estimate velocities over time Not very precise, but works good enough most of the time.
+ *
+ * @param data
+ */
+ private void tickVelocities(MovingData data)
+ {
+
+ /**
+ * ****** DO GENERAL DATA MODIFICATIONS ONCE FOR EACH EVENT ****
+ */
+ if (data.horizVelocityCounter > 0)
+ {
+ data.horizVelocityCounter--;
+ }
+ else if (data.horizFreedom > 0.001)
+ {
+ data.horizFreedom *= 0.90;
+ }
+
+ if (data.vertVelocity <= 0.1)
+ {
+ data.vertVelocityCounter--;
+ }
+ if (data.vertVelocityCounter > 0)
+ {
+ data.vertFreedom += data.vertVelocity;
+ data.vertVelocity *= 0.90;
+ }
+ else if (data.vertFreedom > 0.001)
+ {
+ // Counter has run out, now reduce the vert freedom over time
+ data.vertFreedom *= 0.93;
+ }
+ }
+
+ /**
+ * Player got a velocity packet. The server can't keep track of actual velocity values (by design), so we have to
+ * try and do that ourselves. Very rough estimates.
+ *
+ * @param event The PlayerVelocityEvent
+ */
+ @EventHandler(priority = EventPriority.MONITOR)
+ public void velocity(final PlayerVelocityEvent event)
+ {
+ if (event.isCancelled())
+ {
+ return;
+ }
+
+ final MovingData data = MovingCheck.getData(plugin.getPlayer(event.getPlayer()));
+
+ final Vector v = event.getVelocity();
+
+ double newVal = v.getY();
+ if (newVal >= 0.0D)
+ {
+ data.vertVelocity += newVal;
+ data.vertFreedom += data.vertVelocity;
+ }
+
+ data.vertVelocityCounter = 50;
+
+ newVal = Math.sqrt(Math.pow(v.getX(), 2) + Math.pow(v.getZ(), 2));
+ if (newVal > 0.0D)
+ {
+ data.horizFreedom += newVal;
+ data.horizVelocityCounter = 30;
+ }
+ }
+
+ public List<String> getActiveChecks(ConfigurationCacheStore cc)
+ {
+ LinkedList<String> s = new LinkedList<String>();
+
+ MovingConfig m = MovingCheck.getConfig(cc);
+
+ if (m.runflyCheck)
+ {
+
+ if (!m.allowFlying)
+ {
+ s.add("moving.runfly");
+ if (m.sneakingCheck)
+ {
+ s.add("moving.sneaking");
+ }
+ if (m.nofallCheck)
+ {
+ s.add("moving.nofall");
+ }
+ }
+ else
+ {
+ s.add("moving.flying");
+ }
+
+ }
+ if (m.morePacketsCheck)
+ {
+ s.add("moving.morepackets");
+ }
+
+ return s;
+ }
+}