diff --git a/pom.xml b/pom.xml
index 6f956fe..72262f0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -58,7 +58,13 @@
shade
- false
+ true
+
+
+ com.google.common
+ dev.brighten.ac.com.google.common
+
+
@@ -103,6 +109,12 @@
8.5.6
compile
+
+ com.google.guava
+ guava
+ 31.1-jre
+ compile
+
org.ow2.asm
asm
diff --git a/src/main/java/dev/brighten/ac/Anticheat.java b/src/main/java/dev/brighten/ac/Anticheat.java
index c33e455..506163b 100644
--- a/src/main/java/dev/brighten/ac/Anticheat.java
+++ b/src/main/java/dev/brighten/ac/Anticheat.java
@@ -2,28 +2,35 @@ package dev.brighten.ac;
import co.aikar.commands.BaseCommand;
import co.aikar.commands.BukkitCommandManager;
+import dev.brighten.ac.check.CheckManager;
+import dev.brighten.ac.data.PlayerRegistry;
+import dev.brighten.ac.handler.PacketHandler;
+import dev.brighten.ac.handler.keepalive.KeepaliveProcessor;
import dev.brighten.ac.packet.handler.HandlerAbstract;
import dev.brighten.ac.packet.listener.PacketProcessor;
import dev.brighten.ac.utils.*;
+import dev.brighten.ac.utils.math.RollingAverageDouble;
import dev.brighten.ac.utils.objects.RemoteClassLoader;
import dev.brighten.ac.utils.reflections.Reflections;
import dev.brighten.ac.utils.reflections.types.WrappedClass;
import dev.brighten.ac.utils.reflections.types.WrappedField;
import dev.brighten.ac.utils.reflections.types.WrappedMethod;
+import dev.brighten.ac.utils.timer.Timer;
+import dev.brighten.ac.utils.timer.impl.TickTimer;
import lombok.Getter;
+import lombok.experimental.PackagePrivate;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
-import org.bukkit.scheduler.BukkitRunnable;
-import java.util.Arrays;
-import java.util.Comparator;
-import java.util.Set;
+import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
@Getter
@Init
@@ -34,7 +41,18 @@ public class Anticheat extends JavaPlugin {
private ScheduledExecutorService scheduler;
private PacketProcessor packetProcessor;
private BukkitCommandManager commandManager;
- private int currentTicks;
+ private CheckManager checkManager;
+ private PlayerRegistry playerRegistry;
+ private KeepaliveProcessor keepaliveProcessor;
+ private PacketHandler packetHandler;
+ private int currentTick;
+ private Deque onTickEnd = new LinkedList<>();
+ private ServerInjector injector;
+ //Lag Information
+ private Timer lastTickLag;
+ private long lastTick;
+ @PackagePrivate
+ private RollingAverageDouble tps = new RollingAverageDouble(4, 20);
@ConfigSetting(path = "logging", name = "verbose")
private static boolean verboseLogging = true;
@@ -48,23 +66,43 @@ public class Anticheat extends JavaPlugin {
commandManager = new BukkitCommandManager(this);
+ this.checkManager = new CheckManager();
+ this.playerRegistry = new PlayerRegistry();
+ this.keepaliveProcessor = new KeepaliveProcessor();
+ this.packetHandler = new PacketHandler();
+
+ injector = new ServerInjector();
+ try {
+ injector.inject();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
initializeScanner(getClass(), this,
null,
true,
true);
-
- new BukkitRunnable() {
- public void run() {
- currentTicks++;
- }
- }.runTaskTimer(this, 1L, 1L);
}
public void onDisable() {
scheduler.shutdown();
commandManager.unregisterCommands();
+
+ // Unregistering packet listeners for players
+ Bukkit.getOnlinePlayers().forEach(HandlerAbstract.getHandler()::remove);
+
HandlerList.unregisterAll(this);
packetProcessor.shutdown();
+ checkManager = null;
+
+ // Unregistering APlayer objects
+ playerRegistry.unregisterAll();
+ playerRegistry = null;
+ try {
+ injector.eject();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
}
public void initializeScanner(Class extends Plugin> mainClass, Plugin plugin, ClassLoader loader,
@@ -193,4 +231,33 @@ public class Anticheat extends JavaPlugin {
else MiscUtils.printToConsole(log);
}
}
+
+ public double getTps() {
+ return this.tps.getAverage();
+ }
+
+ public void runTpsTask() {
+ lastTickLag = new TickTimer();
+ AtomicInteger ticks = new AtomicInteger();
+ AtomicLong lastTimeStamp = new AtomicLong(0);
+ RunUtils.taskTimer(() -> {
+ ticks.getAndIncrement();
+ currentTick++;
+ long currentTime = System.currentTimeMillis();
+
+ if(currentTime - lastTick > 120) {
+ lastTickLag.reset();
+ }
+ if(ticks.get() >= 10) {
+ ticks.set(0);
+ tps.add(500D / (currentTime - lastTimeStamp.get()) * 20);
+ lastTimeStamp.set(currentTime);
+ }
+ lastTick = currentTime;
+ }, this, 1L, 1L);
+ }
+
+ public void onTickEnd(Runnable runnable) {
+ onTickEnd.add(runnable);
+ }
}
diff --git a/src/main/java/dev/brighten/ac/check/Action.java b/src/main/java/dev/brighten/ac/check/Action.java
new file mode 100644
index 0000000..9ed2e3b
--- /dev/null
+++ b/src/main/java/dev/brighten/ac/check/Action.java
@@ -0,0 +1,8 @@
+package dev.brighten.ac.check;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Action {
+}
diff --git a/src/main/java/dev/brighten/ac/check/Check.java b/src/main/java/dev/brighten/ac/check/Check.java
new file mode 100644
index 0000000..7457256
--- /dev/null
+++ b/src/main/java/dev/brighten/ac/check/Check.java
@@ -0,0 +1,95 @@
+package dev.brighten.ac.check;
+
+import dev.brighten.ac.Anticheat;
+import dev.brighten.ac.data.APlayer;
+import dev.brighten.ac.utils.Color;
+import dev.brighten.ac.utils.MathUtils;
+import dev.brighten.ac.utils.MiscUtils;
+import dev.brighten.ac.utils.timer.Timer;
+import dev.brighten.ac.utils.timer.impl.MillisTimer;
+import lombok.Getter;
+import lombok.val;
+import net.md_5.bungee.api.chat.HoverEvent;
+import net.md_5.bungee.api.chat.TextComponent;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Getter
+public abstract class Check {
+
+ private final APlayer player;
+
+ private final CheckData checkData;
+ private int vl;
+ private long lastFlagRun;
+ private Timer lastAlert = new MillisTimer();
+
+ public Check(APlayer player) {
+ this.player = player;
+ this.checkData = getClass().getAnnotation(CheckData.class);
+ }
+
+ private String formatAlert(String toFormat, String info) {
+ return addPlaceHolders(Color.translate(toFormat.replace("%desc%", String.join("\n",
+ MiscUtils
+ .splitIntoLine("", 20))))
+ .replace("%info%", info));
+ }
+
+ private String addPlaceHolders(String string) {
+ return string.replace("%player%", player.getBukkitPlayer().getName())
+ .replace("%check%", checkData.name())
+ .replace("%name%", player.getBukkitPlayer().getName())
+ .replace("%vl%", String.valueOf(MathUtils.round(vl, 1)));
+ }
+
+ public void flag(boolean devAlerts, int resetVLTime, String information, Object... variables) {
+ vl++;
+ Anticheat.INSTANCE.getScheduler().execute(() -> {
+ if(Anticheat.INSTANCE.getTps() < 18)
+ vl = 0;
+
+ if(System.currentTimeMillis() - lastFlagRun < 50L) return;
+ lastFlagRun = System.currentTimeMillis();
+
+ final String finalInformation = String.format(information, variables);
+
+ boolean dev = devAlerts || Anticheat.INSTANCE.getTps() < 18;
+ final String info = finalInformation
+ .replace("%p", String.valueOf(player.getLagInfo().getTransPing()))
+ .replace("%t", String.valueOf(MathUtils.round(Anticheat.INSTANCE.getTps(), 2)));
+ //if(vl > 0) Anticheat.INSTANCE.loggerManager.addLog(player, this, info);
+
+ if (true) {
+ //Sending Discord webhook alert
+
+ List components = new ArrayList<>();
+
+ if(dev) {
+ components.add(new TextComponent(createTxt("&8[&cDev&8] ")));
+ }
+ val text = createTxt("&8[&4!&8] &f%player% &7flagged &f%check%" +
+ " &8(&ex%vl%&8)", info);
+
+ text.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new TextComponent[] {
+ createTxt("&eDescription&8: &f%desc%" +
+ "\n&eInfo: &f%info%\n&r\n&7&oClick to teleport to player.", info)}));
+
+ components.add(text);
+
+ TextComponent[] toSend = components.toArray(new TextComponent[0]);
+
+ player.getBukkitPlayer().spigot().sendMessage(toSend);
+ lastAlert.reset();
+ }
+ });
+ }
+
+ private TextComponent createTxt(String txt) {
+ return createTxt(txt, "");
+ }
+ private TextComponent createTxt(String txt, String info) {
+ return new TextComponent(TextComponent.fromLegacyText(Color.translate(formatAlert(txt, info))));
+ }
+}
diff --git a/src/main/java/dev/brighten/ac/check/CheckData.java b/src/main/java/dev/brighten/ac/check/CheckData.java
new file mode 100644
index 0000000..ffaca6d
--- /dev/null
+++ b/src/main/java/dev/brighten/ac/check/CheckData.java
@@ -0,0 +1,10 @@
+package dev.brighten.ac.check;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface CheckData {
+ String name();
+ CheckType type();
+}
diff --git a/src/main/java/dev/brighten/ac/check/CheckManager.java b/src/main/java/dev/brighten/ac/check/CheckManager.java
new file mode 100644
index 0000000..abefe84
--- /dev/null
+++ b/src/main/java/dev/brighten/ac/check/CheckManager.java
@@ -0,0 +1,58 @@
+package dev.brighten.ac.check;
+
+import dev.brighten.ac.Anticheat;
+import dev.brighten.ac.utils.ClassScanner;
+import dev.brighten.ac.utils.Tuple;
+import dev.brighten.ac.utils.reflections.types.WrappedClass;
+import dev.brighten.ac.utils.reflections.types.WrappedMethod;
+import lombok.Getter;
+
+import java.util.*;
+
+@Getter
+public class CheckManager {
+ private final List checkClasses = new ArrayList<>();
+ private final Map>, WrappedMethod[]> events = new HashMap<>();
+
+ public CheckManager() {
+ for (WrappedClass aClass : ClassScanner.getClasses(CheckData.class,
+ "dev.brighten.ac.check.impl")) {
+ addCheck(aClass);
+ }
+ }
+
+ public void addCheck(WrappedClass checkClass) {
+ CheckStatic check = new CheckStatic(checkClass);
+
+ if(!check.getCheckClass().isAnnotationPresent(CheckData.class)) {
+ return;
+ }
+
+ CheckData checkData = check.getCheckClass().getAnnotation(CheckData.class);
+
+ Anticheat.INSTANCE.alog(true, "&7Adding check to CheckManager: " + checkData.name());
+
+ synchronized (events) {
+ // Loop through all the methods in Check that contain @Action annotation by Class
+ check.getEvents().forEach((actionClass, methods) -> {
+ // Check if array is already cached for Class and return Array if so, if not create new Array
+ events.compute(new Tuple>(checkData.name(), actionClass), (packetClass, array) -> {
+ if(array == null) array = new WrappedMethod[0];
+
+ // Adding preexisting cached WrappedMethod into List for further additions
+ List methodList = new ArrayList<>(Arrays.asList(array));
+
+ // Adding all precached Check-specific WrappedMethod into global cache of WrappedMethods.
+ methodList.addAll(methods);
+
+ System.out.println("Registering " + packetClass.toString());
+
+ // Returning newly created array for use in detections.
+ return methodList.toArray(new WrappedMethod[0]);
+ });
+ });
+ }
+
+ checkClasses.add(check);
+ }
+}
diff --git a/src/main/java/dev/brighten/ac/check/CheckStatic.java b/src/main/java/dev/brighten/ac/check/CheckStatic.java
new file mode 100644
index 0000000..7497d57
--- /dev/null
+++ b/src/main/java/dev/brighten/ac/check/CheckStatic.java
@@ -0,0 +1,51 @@
+package dev.brighten.ac.check;
+
+import dev.brighten.ac.data.APlayer;
+import dev.brighten.ac.packet.wrapper.WPacket;
+import dev.brighten.ac.utils.reflections.types.WrappedClass;
+import dev.brighten.ac.utils.reflections.types.WrappedConstructor;
+import dev.brighten.ac.utils.reflections.types.WrappedMethod;
+import lombok.Getter;
+import net.minecraft.server.v1_8_R3.Packet;
+import org.bukkit.event.Event;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class CheckStatic {
+ @Getter
+ private final WrappedClass checkClass;
+ private WrappedConstructor initConst;
+ @Getter
+ private final Map, List> events = new HashMap<>();
+
+ public CheckStatic(WrappedClass checkClass) {
+ this.checkClass = checkClass;
+ processClass();
+ }
+
+ private void processClass() {
+ initConst = checkClass.getConstructor(APlayer.class);
+ for (WrappedMethod method : checkClass.getDeclaredMethods()) {
+ if(!method.isAnnotationPresent(Action.class)
+ || method.getParameters().length == 0) continue;
+ Class> type = method.getParameterTypes()[0];
+
+ if(Packet.class.isAssignableFrom(type)
+ || WPacket.class.isAssignableFrom(type) || Event.class.isAssignableFrom(type)) {
+ events.compute(type, (key, list) -> {
+ if(list == null) list = new ArrayList<>();
+
+ list.add(method);
+ return list;
+ });
+ }
+ }
+ }
+
+ public Check playerInit(APlayer player) {
+ return initConst.newInstance(player);
+ }
+}
diff --git a/src/main/java/dev/brighten/ac/check/CheckType.java b/src/main/java/dev/brighten/ac/check/CheckType.java
new file mode 100644
index 0000000..5d7bfb8
--- /dev/null
+++ b/src/main/java/dev/brighten/ac/check/CheckType.java
@@ -0,0 +1,9 @@
+package dev.brighten.ac.check;
+
+public enum CheckType {
+ COMBAT,
+ MOVEMENT,
+ BLOCK,
+ INTERACT,
+ ORDER
+}
diff --git a/src/main/java/dev/brighten/ac/check/impl/join/JoinCheck.java b/src/main/java/dev/brighten/ac/check/impl/join/JoinCheck.java
new file mode 100644
index 0000000..bd05642
--- /dev/null
+++ b/src/main/java/dev/brighten/ac/check/impl/join/JoinCheck.java
@@ -0,0 +1,21 @@
+package dev.brighten.ac.check.impl.join;
+
+import dev.brighten.ac.check.Action;
+import dev.brighten.ac.check.Check;
+import dev.brighten.ac.check.CheckData;
+import dev.brighten.ac.check.CheckType;
+import dev.brighten.ac.data.APlayer;
+import org.bukkit.Bukkit;
+import org.bukkit.event.player.PlayerJoinEvent;
+
+@CheckData(name = "Join", type = CheckType.ORDER)
+public class JoinCheck extends Check {
+ public JoinCheck(APlayer player) {
+ super(player);
+ }
+
+ @Action
+ public void onJoin(PlayerJoinEvent event) {
+ Bukkit.broadcastMessage("Player joined server: " + getPlayer().getBukkitPlayer().getName());
+ }
+}
diff --git a/src/main/java/dev/brighten/ac/check/impl/speed/Speed.java b/src/main/java/dev/brighten/ac/check/impl/speed/Speed.java
new file mode 100644
index 0000000..555c4d6
--- /dev/null
+++ b/src/main/java/dev/brighten/ac/check/impl/speed/Speed.java
@@ -0,0 +1,272 @@
+package dev.brighten.ac.check.impl.speed;
+
+import dev.brighten.ac.check.Action;
+import dev.brighten.ac.check.Check;
+import dev.brighten.ac.check.CheckData;
+import dev.brighten.ac.check.CheckType;
+import dev.brighten.ac.data.APlayer;
+import dev.brighten.ac.packet.ProtocolVersion;
+import dev.brighten.ac.packet.wrapper.in.WPacketPlayInFlying;
+import dev.brighten.ac.utils.BlockUtils;
+import dev.brighten.ac.utils.MathHelper;
+import org.bukkit.block.Block;
+import org.bukkit.craftbukkit.v1_8_R3.util.CraftMagicNumbers;
+import org.bukkit.potion.PotionEffectType;
+
+@CheckData(name = "Speed", type = CheckType.MOVEMENT)
+public class Speed extends Check {
+ private boolean lastLastClientGround;
+ private float buffer;
+
+ private static boolean[] TRUE_FALSE = new boolean[]{true, false};
+ private static float[] VALUES = new float[]{-0.98f, 0f, 0.98f};
+
+ public Speed(APlayer player) {
+ super(player);
+ }
+
+ @Action
+ public void onFlying(WPacketPlayInFlying packet) {
+
+ Block underBlock = BlockUtils.getBlock(getPlayer().getMovement().getTo().getLoc()
+ .toLocation(getPlayer().getBukkitPlayer().getWorld())
+ .subtract(0, 1, 0)),
+ lastUnderBlock = BlockUtils.getBlock(getPlayer().getMovement().getFrom().getLoc()
+ .toLocation(getPlayer().getBukkitPlayer().getWorld())
+ .subtract(0, 1, 0));
+
+ if (underBlock == null || lastUnderBlock == null) {
+ getPlayer().getBukkitPlayer().sendMessage("Null");
+ return;
+ }
+
+ float friction = CraftMagicNumbers.getBlock(underBlock).frictionFactor,
+ lfriction = CraftMagicNumbers.getBlock(lastUnderBlock).frictionFactor;
+
+ check:
+ {
+ if (!packet.isMoved()
+ /*|| getPlayer().playerInfo.generalCancel
+ || getPlayer().playerInfo.onLadder
+ || getPlayer().playerInfo.lastEntityCollision.isNotPassed(2)
+ || getPlayer().playerInfo.lastVelocity.isNotPassed(1)*/
+ || getPlayer().getBlockInformation().inLiquid
+ || getPlayer().getBlockInformation().collidesHorizontally) {
+ break check;
+ }
+ double smallestDelta = Double.MAX_VALUE;
+
+ double pmotionx = 0, pmotionz = 0;
+ boolean onGround = getPlayer().getMovement().getFrom().isOnGround();
+ loop:
+ {
+ for (int f = -1; f < 2; f++) {
+ for (int s = -1; s < 2; s++) {
+ for (boolean sprinting : TRUE_FALSE) {
+ for (int fastMath = 0; fastMath <= 2; fastMath++) {
+ for (boolean attack : TRUE_FALSE) {
+ for (boolean using : TRUE_FALSE) {
+ for (boolean sneaking : TRUE_FALSE) {
+ for (boolean jumped : TRUE_FALSE) {
+
+ float forward = f, strafe = s;
+
+ if (sneaking) {
+ forward *= 0.3;
+ strafe *= 0.3;
+ }
+
+ if (using) {
+ forward *= 0.2;
+ strafe *= 0.2;
+ }
+
+ //Multiplying by 0.98 like in client
+ forward *= 0.9800000190734863F;
+ strafe *= 0.9800000190734863F;
+
+ double aiMoveSpeed = getPlayer().getBukkitPlayer().getWalkSpeed() / 2;
+
+ float drag = 0.91f;
+ double lmotionX = getPlayer().getMovement().getLDeltaX(),
+ lmotionZ = getPlayer().getMovement().getLDeltaZ();
+
+ //The "1" will effectively remove lastFriction from the equation
+ lmotionX *= (lastLastClientGround ? lfriction : 1) * 0.9100000262260437D;
+ lmotionZ *= (lastLastClientGround ? lfriction : 1) * 0.9100000262260437D;
+
+ //Running multiplication done after previous prediction
+ if (getPlayer().getPlayerVersion().isOrAbove(ProtocolVersion.V1_9)) {
+ if (Math.abs(lmotionX) < 0.003)
+ lmotionX = 0;
+ if (Math.abs(lmotionZ) < 0.003)
+ lmotionZ = 0;
+ } else {
+ if (Math.abs(lmotionX) < 0.005)
+ lmotionX = 0;
+ if (Math.abs(lmotionZ) < 0.005)
+ lmotionZ = 0;
+ }
+
+ // Attack slowdown
+ if (attack) {
+ lmotionX *= 0.6;
+ lmotionZ *= 0.6;
+ }
+
+ if (sprinting) aiMoveSpeed += aiMoveSpeed * 0.30000001192092896D;
+
+ if (getPlayer().getPotionHandler().hasPotionEffect(PotionEffectType.SPEED))
+ aiMoveSpeed += (getPlayer().getPotionHandler().getEffectByType(PotionEffectType.SPEED)
+ .get()
+ .getAmplifier() + 1) * (double) 0.20000000298023224D * aiMoveSpeed;
+ if (getPlayer().getPotionHandler().hasPotionEffect(PotionEffectType.SLOW))
+ aiMoveSpeed += (getPlayer().getPotionHandler().getEffectByType(PotionEffectType.SLOW)
+ .get()
+ .getAmplifier() + 1) * (double) -0.15000000596046448D * aiMoveSpeed;
+
+ float f5;
+ if (onGround) {
+ drag *= friction;
+
+ f5 = (float) (aiMoveSpeed * (0.16277136F / (drag * drag * drag)));
+
+ if (sprinting && jumped) {
+ float rot = getPlayer().getMovement().getTo().getLoc().yaw * 0.017453292F;
+ lmotionX -= sin(fastMath, rot) * 0.2F;
+ lmotionZ += cos(fastMath, rot) * 0.2F;
+ }
+
+ } else f5 = sprinting ? 0.025999999F : 0.02f;
+
+ if (getPlayer().getPlayerVersion().isOrAbove(ProtocolVersion.V1_9)) {
+ double keyedMotion = forward * forward + strafe * strafe;
+
+ if (keyedMotion >= 1.0E-4F) {
+ keyedMotion = f5 / Math.max(1.0, Math.sqrt(keyedMotion));
+ forward *= keyedMotion;
+ strafe *= keyedMotion;
+
+ final float yawSin = sin(fastMath,
+ getPlayer().getMovement().getTo().getLoc().yaw * (float) Math.PI / 180.F),
+ yawCos = cos(fastMath,
+ getPlayer().getMovement().getTo().getLoc().yaw * (float) Math.PI / 180.F);
+
+ lmotionX += (strafe * yawCos - forward * yawSin);
+ lmotionZ += (forward * yawCos + strafe * yawSin);
+ }
+ } else {
+ float keyedMotion = forward * forward + strafe * strafe;
+
+ if (keyedMotion >= 1.0E-4F) {
+ keyedMotion = f5 / Math.max(1.0f, MathHelper.sqrt_float(keyedMotion));
+ forward *= keyedMotion;
+ strafe *= keyedMotion;
+
+ final float yawSin = sin(fastMath,
+ getPlayer().getMovement().getTo().getLoc().yaw * (float) Math.PI / 180.F),
+ yawCos = cos(fastMath,
+ getPlayer().getMovement().getTo().getLoc().yaw * (float) Math.PI / 180.F);
+
+ lmotionX += (strafe * yawCos - forward * yawSin);
+ lmotionZ += (forward * yawCos + strafe * yawSin);
+ }
+ }
+
+ double diffX = getPlayer().getMovement().getDeltaX() - lmotionX,
+ diffZ = getPlayer().getMovement().getDeltaZ() - lmotionZ;
+ double delta = (diffX * diffX) + (diffZ * diffZ);
+
+ if (delta < smallestDelta) {
+ smallestDelta = delta;
+ pmotionx = lmotionX;
+ pmotionz = lmotionZ;
+
+ if (delta < 1E-15) {
+ break loop;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ double pmotion = Math.hypot(pmotionx, pmotionz);
+
+ if (getPlayer().getMovement().getDeltaXZ() > pmotion && smallestDelta > 1E-4 && getPlayer().getMovement().getDeltaXZ() > 0.1) {
+ if (++buffer > 3) {
+ buffer = Math.min(3.5f, buffer); //Ensuring we don't have a run-away buffer
+ flag(false,0, "smallest=%s b=%.1f to=%s dxz=%.2f", smallestDelta, buffer, getPlayer().getMovement().getTo().getLoc(), getPlayer().getMovement().getDeltaXZ());
+ }
+ } else if (buffer > 0) buffer -= 0.1f;
+
+ //getPlayer().getBukkitPlayer().sendMessage(String.format("smallest=%s b=%.1f to=%s dxz=%.2f", smallestDelta, buffer, getPlayer().getMovement().getTo().getLoc(), getPlayer().getMovement().getDeltaXZ()));
+
+ //debug("smallest=%s b=%.1f", smallestDelta, buffer);
+ }
+ lastLastClientGround = getPlayer().getMovement().getFrom().isOnGround();
+ }
+
+ private static final float[] SIN_TABLE_FAST = new float[4096], SIN_TABLE_FAST_NEW = new float[4096];
+ private static final float[] SIN_TABLE = new float[65536];
+ private static final float radToIndex = roundToFloat(651.8986469044033D);
+
+ public static float sin(int type, float value) {
+ switch(type) {
+ case 0:
+ default: {
+ return SIN_TABLE[(int) (value * 10430.378F) & 65535];
+ }
+ case 1: {
+ return SIN_TABLE_FAST[(int) (value * 651.8986F) & 4095];
+ }
+ case 2: {
+ return SIN_TABLE_FAST_NEW[(int)(value * radToIndex) & 4095];
+ }
+ }
+ }
+
+ public static float cos(int type, float value) {
+ switch (type) {
+ case 0:
+ default:
+ return SIN_TABLE[(int) (value * 10430.378F + 16384.0F) & 65535];
+ case 1:
+ return SIN_TABLE_FAST[(int) ((value + ((float) Math.PI / 2F)) * 651.8986F) & 4095];
+ case 2:
+ return SIN_TABLE_FAST_NEW[(int)(value * radToIndex + 1024.0F) & 4095];
+ }
+ }
+
+ static {
+ for (int i = 0; i < 65536; ++i)
+ {
+ SIN_TABLE[i] = (float)Math.sin((double)i * Math.PI * 2.0D / 65536.0D);
+ }
+
+ for (int j = 0; j < 4096; ++j)
+ {
+ SIN_TABLE_FAST[j] = (float)Math.sin((double)(((float)j + 0.5F) / 4096.0F * ((float)Math.PI * 2F)));
+ }
+
+ for (int l = 0; l < 360; l += 90)
+ {
+ SIN_TABLE_FAST[(int)((float)l * 11.377778F) & 4095] = (float)Math.sin((double)((float)l * 0.017453292F));
+ }
+
+ for (int j = 0; j < SIN_TABLE_FAST_NEW.length; ++j)
+ {
+ SIN_TABLE_FAST_NEW[j] = roundToFloat(Math.sin((double)j * Math.PI * 2.0D / 4096.0D));
+ }
+ }
+
+ private static float roundToFloat(double d)
+ {
+ return (float)((double)Math.round(d * 1.0E8D) / 1.0E8D);
+ }
+}
diff --git a/src/main/java/dev/brighten/ac/data/APlayer.java b/src/main/java/dev/brighten/ac/data/APlayer.java
new file mode 100644
index 0000000..2e18c84
--- /dev/null
+++ b/src/main/java/dev/brighten/ac/data/APlayer.java
@@ -0,0 +1,155 @@
+package dev.brighten.ac.data;
+
+import dev.brighten.ac.Anticheat;
+import dev.brighten.ac.check.Check;
+import dev.brighten.ac.check.CheckStatic;
+import dev.brighten.ac.data.handlers.BlockInformation;
+import dev.brighten.ac.data.handlers.GeneralInformation;
+import dev.brighten.ac.data.handlers.LagInformation;
+import dev.brighten.ac.data.handlers.MovementHandler;
+import dev.brighten.ac.data.obj.InstantAction;
+import dev.brighten.ac.data.obj.NormalAction;
+import dev.brighten.ac.handler.PotionHandler;
+import dev.brighten.ac.handler.keepalive.KeepAlive;
+import dev.brighten.ac.packet.ProtocolVersion;
+import dev.brighten.ac.packet.handler.HandlerAbstract;
+import dev.brighten.ac.utils.Tuple;
+import dev.brighten.ac.utils.reflections.types.WrappedMethod;
+import lombok.Getter;
+import lombok.val;
+import net.minecraft.server.v1_8_R3.PacketPlayOutTransaction;
+import org.bukkit.entity.Player;
+import org.bukkit.event.Event;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.function.Consumer;
+
+
+public class APlayer {
+ @Getter
+ private final Player bukkitPlayer;
+ private final List checks = new ArrayList<>();
+ @Getter
+ private MovementHandler movement;
+ @Getter
+ private PotionHandler potionHandler;
+ @Getter
+ private GeneralInformation info;
+ @Getter
+ private LagInformation lagInfo;
+ @Getter
+ private BlockInformation blockInformation;
+ @Getter
+ private int playerTick;
+ @Getter
+ //TODO Actually grab real player version once finished implementing version grabber from Atlas
+ private ProtocolVersion playerVersion = ProtocolVersion.V1_8_9;
+
+ public final Map>> instantTransaction = new HashMap<>();
+ public final List keepAliveStamps = new ArrayList<>();
+
+ public APlayer(Player player) {
+ this.bukkitPlayer = player;
+
+ load();
+ }
+
+ private void load() {
+ for (CheckStatic check : Anticheat.INSTANCE.getCheckManager().getCheckClasses()) {
+ checks.add(check.playerInit(this));
+ }
+ this.movement = new MovementHandler(this);
+ this.potionHandler = new PotionHandler(this);
+ this.info = new GeneralInformation();
+ this.lagInfo = new LagInformation();
+ this.blockInformation = new BlockInformation(this);
+ }
+
+ protected void unload() {
+ checks.clear();
+ this.info = null;
+ this.lagInfo = null;
+ this.movement = null;
+ }
+
+ public void callEvent(Event event) {
+ for (Check check : checks) {
+ WrappedMethod[] methods = Anticheat.INSTANCE.getCheckManager().getEvents()
+ .get(new Tuple>(check.getCheckData().name(), event.getClass()));
+
+ if(methods != null) {
+ for (WrappedMethod method : methods) {
+ method.invoke(check, event);
+ }
+ }
+ }
+ }
+
+ //TODO When using WPacket wrappers only, make this strictly WPacket param based only
+ public void callPacket(Object packet) {
+ for (Check check : checks) {
+ WrappedMethod[] methods = Anticheat.INSTANCE.getCheckManager().getEvents()
+ .get(new Tuple>(check.getCheckData().name(), packet.getClass()));
+
+ if(methods != null) {
+
+ for (WrappedMethod method :
+ methods) {
+ method.invoke(check, packet);
+ }
+ }
+ }
+ }
+
+ public int runKeepaliveAction(Consumer action) {
+ return runKeepaliveAction(action, 0);
+ }
+
+ public int runKeepaliveAction(Consumer action, int later) {
+ int id = Anticheat.INSTANCE.getKeepaliveProcessor().currentKeepalive.start + later;
+
+ keepAliveStamps.add(new NormalAction(id, action));
+
+ return id;
+ }
+
+
+ public void runInstantAction(Consumer runnable) {
+ runInstantAction(runnable, false);
+ }
+
+ public void runInstantAction(Consumer runnable, boolean flush) {
+ short startId = (short) ThreadLocalRandom.current().nextInt(Short.MIN_VALUE, Short.MAX_VALUE),
+ endId = (short)(startId + 1);
+
+ //Ensuring we don't have any duplicate IDS
+ val map = Anticheat.INSTANCE.getKeepaliveProcessor().keepAlives.asMap();
+ while (map.containsKey(startId)
+ || map.containsKey(endId)) {
+ startId = (short) ThreadLocalRandom.current().nextInt(Short.MIN_VALUE, Short.MAX_VALUE);
+ endId = (short)(startId + (short)1);
+ }
+
+ InstantAction startAction = new InstantAction(startId, endId, false);
+ instantTransaction.put(startId, new Tuple<>(startAction, runnable));
+
+ HandlerAbstract.getHandler().sendPacket(this, new PacketPlayOutTransaction(0, startId, false));
+
+ short finalEndId = endId, finalStartId = startId;
+ Anticheat.INSTANCE.onTickEnd(() -> {
+ InstantAction endAction = new InstantAction(finalStartId, finalEndId, true);
+ instantTransaction.put(finalEndId, new Tuple<>(endAction, runnable));
+
+ HandlerAbstract.getHandler()
+ .sendPacket(this, new PacketPlayOutTransaction(0, finalEndId, false));
+ });
+ }
+
+ public void addPlayerTick() {
+ playerTick++;
+ }
+}
diff --git a/src/main/java/dev/brighten/ac/data/PlayerRegistry.java b/src/main/java/dev/brighten/ac/data/PlayerRegistry.java
new file mode 100644
index 0000000..6843934
--- /dev/null
+++ b/src/main/java/dev/brighten/ac/data/PlayerRegistry.java
@@ -0,0 +1,47 @@
+package dev.brighten.ac.data;
+
+import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
+import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
+import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+
+import java.util.Optional;
+import java.util.UUID;
+
+public class PlayerRegistry {
+
+ public PlayerRegistry() {
+ Bukkit.getOnlinePlayers().forEach(this::generate);
+ }
+ public final Int2ObjectMap aplayerMap = Int2ObjectMaps.synchronize(new Int2ObjectOpenHashMap<>());
+
+ public Optional getPlayer(UUID uuid) {
+ return Optional.of(aplayerMap.get(uuid.hashCode()));
+ }
+
+ public APlayer generate(Player player) {
+ if(aplayerMap.containsKey(player.getUniqueId().hashCode())) {
+ unregister(player.getUniqueId());
+ }
+
+ synchronized (aplayerMap) {
+ APlayer aplayer = new APlayer(player);
+ aplayerMap.put(player.getUniqueId().hashCode(), aplayer);
+ return aplayer;
+ }
+ }
+
+ public void unregister(UUID uuid) {
+ synchronized (aplayerMap) {
+ Optional.of(aplayerMap.remove(uuid.hashCode())).ifPresent(APlayer::unload);
+ }
+ }
+
+ public void unregisterAll() {
+ synchronized (aplayerMap) {
+ aplayerMap.forEach((key, val) -> val.unload());
+ aplayerMap.clear();
+ }
+ }
+}
diff --git a/src/main/java/dev/brighten/ac/data/handlers/BlockInformation.java b/src/main/java/dev/brighten/ac/data/handlers/BlockInformation.java
new file mode 100644
index 0000000..33b1224
--- /dev/null
+++ b/src/main/java/dev/brighten/ac/data/handlers/BlockInformation.java
@@ -0,0 +1,368 @@
+package dev.brighten.ac.data.handlers;
+
+import dev.brighten.ac.Anticheat;
+import dev.brighten.ac.data.APlayer;
+import dev.brighten.ac.packet.ProtocolVersion;
+import dev.brighten.ac.utils.*;
+import dev.brighten.ac.utils.world.BlockData;
+import dev.brighten.ac.utils.world.CollisionBox;
+import dev.brighten.ac.utils.world.EntityData;
+import dev.brighten.ac.utils.world.types.SimpleCollisionBox;
+import org.bukkit.Chunk;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.World;
+import org.bukkit.block.Block;
+import org.bukkit.entity.Entity;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.List;
+
+public class BlockInformation {
+ private APlayer player;
+ public boolean onClimbable, onSlab, onStairs, onHalfBlock, inLiquid, inLava, inWater, inWeb, onSlime, onIce,
+ onSoulSand, blocksAbove, collidesVertically, bedNear, collidesHorizontally, blocksNear, inBlock, miscNear,
+ collidedWithEntity, roseBush, inPortal, blocksBelow, pistonNear, fenceBelow, inScaffolding, inHoney;
+ public float currentFriction, fromFriction;
+ public CollisionHandler
+ handler = new CollisionHandler(new ArrayList<>(), new ArrayList<>(), new KLocation(0,0,0), null);
+ public final List aboveCollisions = Collections.synchronizedList(new ArrayList<>()),
+ belowCollisions = Collections.synchronizedList(new ArrayList<>());
+ public final List blocks = Collections.synchronizedList(new ArrayList<>());
+ private static EnumMap matchMaterial = new EnumMap<>(Material.class);
+ //Caching material
+ private final Material cobweb = XMaterial.COBWEB.parseMaterial(),
+ rosebush = XMaterial.ROSE_BUSH.parseMaterial(),
+ scaffolding = XMaterial.SCAFFOLDING.parseMaterial(),
+ honey = XMaterial.HONEY_BLOCK.parseMaterial();
+
+ static {
+ for (Material mat : Material.values()) {
+ matchMaterial.put(mat, XMaterial.matchXMaterial(mat));
+ }
+ }
+
+ public static XMaterial getXMaterial(Material material) {
+ return matchMaterial.getOrDefault(material, null);
+ }
+
+ public BlockInformation(APlayer objectData) {
+ this.player = objectData;
+ }
+
+ public void runCollisionCheck() {
+ if(!Anticheat.INSTANCE.isEnabled())
+ return;
+
+ double dy = player.getMovement().getDeltaY();
+ double dh = player.getMovement().getDeltaXZ();
+
+ blocks.clear();
+
+ player.getInfo().setServerGround(false);
+ player.getInfo().setNearGround(false);
+ onClimbable = fenceBelow
+ = inScaffolding = inHoney
+ = onSlab = onStairs = onHalfBlock = inLiquid = inLava = inWater = inWeb = onSlime = pistonNear
+ = onIce = onSoulSand = blocksAbove = collidesVertically = bedNear = collidesHorizontally =
+ blocksNear = inBlock = miscNear = collidedWithEntity = blocksBelow = inPortal = false;
+
+ if(dy > 10) dy = 10;
+ else if(dy < -10) dy = -10;
+ if(dh > 10) dh = 10;
+
+ int startX = Location.locToBlock(player.getMovement().getTo().getLoc().x - 1 - dh);
+ int endX = Location.locToBlock(player.getMovement().getTo().getLoc().x + 1 + dh);
+ int startY = Location.locToBlock(player.getMovement().getTo().getLoc().y - Math.max(0.6, 0.6 + Math.abs(dy)));
+ int endY = Location.locToBlock(player.getMovement().getTo().getLoc().y + Math.max(2.1, 2.1 + Math.abs(dy)));
+ int startZ = Location.locToBlock(player.getMovement().getTo().getLoc().z - 1 - dh);
+ int endZ = Location.locToBlock(player.getMovement().getTo().getLoc().z + 1 + dh);
+
+ SimpleCollisionBox waterBox = player.getMovement().getTo().getBox().copy().expand(0, -.38, 0);
+
+ waterBox.xMin = MathHelper.floor_double(waterBox.xMin);
+ waterBox.yMin = MathHelper.floor_double(waterBox.yMin);
+ waterBox.zMin = MathHelper.floor_double(waterBox.zMin);
+ waterBox.xMax = MathHelper.floor_double(waterBox.xMax + 1.);
+ waterBox.yMax = MathHelper.floor_double(waterBox.yMax + 1.);
+ waterBox.zMax = MathHelper.floor_double(waterBox.zMax + 1.);
+
+ SimpleCollisionBox lavaBox = player.getMovement().getTo().getBox().copy().expand(-.1f, -.4f, -.1f);
+
+ lavaBox.xMin = MathHelper.floor_double(waterBox.xMin);
+ lavaBox.yMin = MathHelper.floor_double(waterBox.yMin);
+ lavaBox.zMin = MathHelper.floor_double(waterBox.zMin);
+ lavaBox.xMax = MathHelper.floor_double(waterBox.xMax + 1.);
+ lavaBox.yMax = MathHelper.floor_double(waterBox.yMax + 1.);
+ lavaBox.zMax = MathHelper.floor_double(waterBox.zMax + 1.);
+
+ SimpleCollisionBox normalBox = player.getMovement().getTo().getBox().copy();
+
+ inWater = MiscUtils.isInMaterialBB(player.getBukkitPlayer().getWorld(), waterBox, Materials.WATER);
+ inLava = MiscUtils.isInMaterialBB(player.getBukkitPlayer().getWorld(), lavaBox, Materials.LAVA);
+ inLiquid = inWater || inLava;
+
+ player.getInfo().setWorldLoaded(true);
+ player.getInfo().setLastServerGround(player.getInfo().isServerGround());
+ synchronized (belowCollisions) {
+ belowCollisions.clear();
+ }
+ synchronized (aboveCollisions) {
+ aboveCollisions.clear();
+ }
+ final World world = player.getBukkitPlayer().getWorld();
+ int it = 9 * 9;
+ start:
+ for (int chunkx = startX >> 4; chunkx <= endX >> 4; ++chunkx) {
+ int cx = chunkx << 4;
+
+ for (int chunkz = startZ >> 4; chunkz <= endZ >> 4; ++chunkz) {
+ if (!world.isChunkLoaded(chunkx, chunkz)) {
+ player.getInfo().setWorldLoaded(false);
+ continue;
+ }
+ Chunk chunk = world.getChunkAt(chunkx, chunkz);
+ if (chunk != null) {
+ int cz = chunkz << 4;
+ int xstart = Math.max(startX, cx);
+ int xend = Math.min(endX, cx + 16);
+ int zstart = Math.max(startZ, cz);
+ int zend = Math.min(endZ, cz + 16);
+
+ for (int x = xstart; x <= xend; ++x) {
+ for (int z = zstart; z <= zend; ++z) {
+ for (int y = Math.max(-50, startY); y <= endY; ++y) {
+ if (it-- <= 0) {
+ break start;
+ }
+ if(y > 400 || y < -50) continue;
+ Block block = chunk.getBlock(x & 15, y, z & 15);
+ final Material type = block.getType();
+ if (type != Material.AIR) {
+ blocks.add(block);
+
+ CollisionBox blockBox = BlockData.getData(type)
+ .getBox(block, player.getPlayerVersion());
+
+ if(blockBox.isCollided(normalBox)) {
+ if(type.equals(cobweb))
+ inWeb = true;
+ else if(type.equals(scaffolding)) inScaffolding = true;
+ else if(type.equals(honey)) inHoney = true;
+ }
+
+ if(type.equals(rosebush))
+ roseBush = true;
+
+ if(normalBox.copy().offset(0, 0.6f, 0).isCollided(blockBox))
+ blocksAbove = true;
+
+ if(normalBox.copy().expand(1, -0.0001, 1).isIntersected(blockBox))
+ blocksNear = true;
+
+ if(normalBox.copy().expand(0.1, 0, 0.1)
+ .offset(0, 1,0).isCollided(blockBox)) {
+ synchronized (aboveCollisions) {
+ blockBox.downCast(aboveCollisions);
+ }
+ }
+
+ if(normalBox.copy().expand(0.1, 0, 0.1).offset(0, -1, 0)
+ .isCollided(blockBox)) {
+ synchronized (belowCollisions) {
+ blockBox.downCast(belowCollisions);
+ }
+
+ if(Materials.checkFlag(type, Materials.FENCE)
+ || Materials.checkFlag(type, Materials.WALL)) {
+ fenceBelow = true;
+ }
+ }
+
+ if(Materials.checkFlag(type, Materials.SOLID)) {
+ SimpleCollisionBox groundBox = normalBox.copy()
+ .offset(0, -.49, 0).expandMax(0, -1.2, 0);
+
+ XMaterial blockMaterial = getXMaterial(type);
+
+ if(normalBox.copy().expand(0.4, 0, 0.4).expandMin(0, -1, 0)
+ .isIntersected(blockBox))
+ blocksBelow = true;
+
+ if(normalBox.isIntersected(blockBox)) inBlock = true;
+
+ SimpleCollisionBox box = player.getMovement().getTo().getBox().copy();
+
+ box.expand(Math.abs(player.getMovement().getDeltaX()) + 0.1, -0.001,
+ Math.abs(player.getMovement().getDeltaZ()) + 0.1);
+ if (blockBox.isCollided(box))
+ collidesHorizontally = true;
+
+ box = player.getMovement().getTo().getBox().copy();
+ box.expand(0, 0.1, 0);
+
+ if (blockBox.isCollided(box))
+ collidesVertically = true;
+
+ if(groundBox.copy().expandMin(0, -0.8, 0).expand(0.2, 0, 0.2)
+ .isIntersected(blockBox))
+ player.getInfo().setNearGround(true);
+
+ if(groundBox.isCollided(blockBox)) {
+ player.getInfo().setServerGround(true);
+
+ if(blockMaterial != null)
+ switch (blockMaterial) {
+ case ICE:
+ case BLUE_ICE:
+ case FROSTED_ICE:
+ case PACKED_ICE: {
+ onIce = true;
+ break;
+ }
+ case SOUL_SAND: {
+ onSoulSand = true;
+ break;
+ }
+ case SLIME_BLOCK: {
+ onSlime = true;
+ break;
+ }
+ }
+ }
+ if(player.getMovement().getDeltaY() > 0
+ && player.getPlayerVersion().isBelow(ProtocolVersion.V1_14)
+ && Materials.checkFlag(type, Materials.LADDER)
+ && normalBox.copy().expand(0.2f, 0, 0.2f)
+ .isCollided(blockBox)) {
+ onClimbable = true;
+ }
+
+ if(blockMaterial != null) {
+ switch (blockMaterial) {
+ case PISTON:
+ case PISTON_HEAD:
+ case MOVING_PISTON:
+ case STICKY_PISTON: {
+ if(normalBox.copy().expand(0.5, 0.5, 0.5)
+ .isCollided(blockBox))
+ pistonNear = true;
+ break;
+ }
+ }
+ }
+
+ if(groundBox.copy().expand(0.5, 0.3, 0.5).isCollided(blockBox)) {
+ if(Materials.checkFlag(type, Materials.SLABS))
+ onSlab = true;
+ else
+ if(Materials.checkFlag(type, Materials.STAIRS))
+ onStairs = true;
+ else
+ if(blockMaterial != null)
+ switch(blockMaterial) {
+ case CAKE:
+ case BREWING_STAND:
+ case FLOWER_POT:
+ case PLAYER_HEAD:
+ case PLAYER_WALL_HEAD:
+ case SKELETON_SKULL:
+ case CREEPER_HEAD:
+ case DRAGON_HEAD:
+ case ZOMBIE_HEAD:
+ case ZOMBIE_WALL_HEAD:
+ case CREEPER_WALL_HEAD:
+ case DRAGON_WALL_HEAD:
+ case WITHER_SKELETON_SKULL:
+ case LANTERN:
+ case SKELETON_WALL_SKULL:
+ case WITHER_SKELETON_WALL_SKULL:
+ case SNOW: {
+ miscNear = true;
+ break;
+ }
+ case BLACK_BED:
+ case BLUE_BED:
+ case BROWN_BED:
+ case CYAN_BED:
+ case GRAY_BED:
+ case GREEN_BED:
+ case LIME_BED:
+ case MAGENTA_BED:
+ case ORANGE_BED:
+ case PINK_BED:
+ case PURPLE_BED:
+ case RED_BED:
+ case WHITE_BED:
+ case YELLOW_BED:
+ case LIGHT_BLUE_BED:
+ case LIGHT_GRAY_BED: {
+ bedNear = true;
+ break;
+ }
+ }
+ }
+ } else if(blockBox.isCollided(normalBox)) {
+ XMaterial blockMaterial = getXMaterial(type);
+
+ if(blockMaterial != null)
+ switch(blockMaterial) {
+ case END_PORTAL:
+ case NETHER_PORTAL: {
+ inPortal = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if(!player.getInfo().isWorldLoaded())
+ return;
+
+ CollisionHandler handler = new CollisionHandler(blocks,
+ player.getInfo().getNearbyEntities(),
+ player.getMovement().getTo().getLoc(), player);
+
+ //Bukkit.broadcastMessage("chigga4");
+
+ for (Entity entity : handler.getEntities()) {
+ CollisionBox entityBox = EntityData.getEntityBox(entity.getLocation(), entity);
+
+ if(entityBox == null) continue;
+
+ if(entityBox.isCollided(normalBox.copy().offset(0, -.1, 0)))
+ player.getInfo().setServerGround(true);
+
+ if(entityBox.isCollided(normalBox))
+ collidedWithEntity = true;
+ }
+
+ //Bukkit.broadcastMessage("chigga5");
+ onHalfBlock = onSlab || onStairs || miscNear || bedNear;
+
+ if((player.getMovement().getDeltaY() <= 0 || player.getPlayerVersion().isOrAbove(ProtocolVersion.V1_14))
+ && !onClimbable) {
+ onClimbable = player.getInfo().getBlockOnTo().isPresent()
+ && BlockUtils.isClimbableBlock(player.getInfo().getBlockOnTo().get());
+ }
+
+ handler.setSize(0.6f, 1.8f);
+ handler.setOffset(0f);
+
+ this.handler.getEntities().clear();
+ this.handler = handler;
+ }
+
+ public SimpleCollisionBox getBox() {
+ return new SimpleCollisionBox(player.getMovement().getTo().getLoc().toVector(), player.getMovement().getTo().getLoc().toVector())
+ .expand(0.3, 0,0.3).expandMax(0, 1.8, 0);
+ }
+}
diff --git a/src/main/java/dev/brighten/ac/data/handlers/GeneralInformation.java b/src/main/java/dev/brighten/ac/data/handlers/GeneralInformation.java
new file mode 100644
index 0000000..24f7e1e
--- /dev/null
+++ b/src/main/java/dev/brighten/ac/data/handlers/GeneralInformation.java
@@ -0,0 +1,25 @@
+package dev.brighten.ac.data.handlers;
+
+import dev.brighten.ac.utils.PastLocation;
+import dev.brighten.ac.utils.timer.Timer;
+import dev.brighten.ac.utils.timer.impl.TickTimer;
+import lombok.Getter;
+import lombok.Setter;
+import org.bukkit.block.Block;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.LivingEntity;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+
+@Getter
+@Setter
+public class GeneralInformation {
+ private Optional blockOnTo, blockBelow;
+ private Timer lastMove = new TickTimer();
+ private LivingEntity target;
+ private boolean serverGround, lastServerGround, nearGround, worldLoaded;
+ private List nearbyEntities = Collections.emptyList();
+ private PastLocation targetPastLocation;
+}
diff --git a/src/main/java/dev/brighten/ac/data/handlers/LagInformation.java b/src/main/java/dev/brighten/ac/data/handlers/LagInformation.java
new file mode 100644
index 0000000..9ee4926
--- /dev/null
+++ b/src/main/java/dev/brighten/ac/data/handlers/LagInformation.java
@@ -0,0 +1,14 @@
+package dev.brighten.ac.data.handlers;
+
+import dev.brighten.ac.utils.timer.Timer;
+import dev.brighten.ac.utils.timer.impl.MillisTimer;
+import dev.brighten.ac.utils.timer.impl.TickTimer;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class LagInformation {
+ private Timer lastPingDrop = new TickTimer(), lastClientTransaction = new MillisTimer();
+ private long transPing, lastTransPing;
+}
diff --git a/src/main/java/dev/brighten/ac/data/handlers/MovementHandler.java b/src/main/java/dev/brighten/ac/data/handlers/MovementHandler.java
new file mode 100644
index 0000000..920abcb
--- /dev/null
+++ b/src/main/java/dev/brighten/ac/data/handlers/MovementHandler.java
@@ -0,0 +1,124 @@
+package dev.brighten.ac.data.handlers;
+
+import dev.brighten.ac.data.APlayer;
+import dev.brighten.ac.data.obj.CMove;
+import dev.brighten.ac.packet.wrapper.in.WPacketPlayInFlying;
+import dev.brighten.ac.utils.BlockUtils;
+import dev.brighten.ac.utils.MathUtils;
+import dev.brighten.ac.utils.objects.evicting.EvictingList;
+import dev.brighten.ac.utils.world.types.SimpleCollisionBox;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import org.bukkit.Location;
+
+import java.util.LinkedList;
+
+@RequiredArgsConstructor
+public class MovementHandler {
+
+ private final APlayer player;
+
+ @Getter
+ private final CMove to = new CMove(), from = new CMove();
+ @Getter
+ private double deltaX, deltaY, deltaZ, deltaXZ,
+ lDeltaX, lDeltaY, lDeltaZ, lDeltaXZ;
+ @Getter
+ private float deltaYaw, deltaPitch, lDeltaYaw, lDeltaPitch;
+ private int moveTicks;
+
+ private int teleportsToConfirm;
+
+ private LinkedList yawGcdList = new EvictingList<>(45),
+ pitchGcdList = new EvictingList<>(45);
+
+
+ public void process(WPacketPlayInFlying packet, long currentTime) {
+ player.getPotionHandler().onFlying(packet);
+ if(moveTicks > 0) {
+ updateLocations(packet);
+
+ // Updating block locations
+ player.getInfo().setBlockOnTo(BlockUtils
+ .getBlockAsync(to.getLoc().toLocation(player.getBukkitPlayer().getWorld())));
+ player.getInfo().setBlockBelow(BlockUtils
+ .getBlockAsync(to.getLoc().toLocation(player.getBukkitPlayer().getWorld())
+ .subtract(0,1,0)));
+
+ if(packet.isMoved()) {
+ // Updating player bounding box
+ player.getInfo().getLastMove().reset();
+ }
+
+ player.getBlockInformation().runCollisionCheck();
+ }
+
+
+ moveTicks++; //Must be end of code
+ }
+
+ /**
+ * Updating the "to" and "from" location to current location.
+ * Resetting position tracking; meant primarily for instant teleports.
+ * @param location Location
+ */
+ public void moveTo(Location location) {
+ to.getLoc().x = from.getLoc().x = location.getX();
+ to.getLoc().y = from.getLoc().y = location.getY();
+ to.getLoc().z = from.getLoc().z = location.getZ();
+ to.getLoc().yaw = from.getLoc().yaw = location.getYaw();
+ to.getLoc().pitch = from.getLoc().pitch = location.getPitch();
+
+ deltaX = deltaY = deltaZ = deltaXZ
+ = lDeltaX = lDeltaY = lDeltaZ
+ = lDeltaXZ = 0;
+
+ deltaYaw = lDeltaYaw =
+ deltaPitch = lDeltaPitch = 0;
+ moveTicks = 0;
+ //doingTeleport = inventoryOpen = false;
+ }
+
+ /**
+ * Setting the to and from to current location only if the player either moved or looked.
+ * @param packet WPacketPlayInFlyingh
+ */
+ private void setTo(WPacketPlayInFlying packet) {
+ to.setWorld(player.getBukkitPlayer().getWorld());
+ if(packet.isMoved()) {
+ to.getLoc().x = packet.getX();
+ to.getLoc().y = packet.getY();
+ to.getLoc().z = packet.getZ();
+ }
+ if(packet.isLooked()) {
+ to.getLoc().yaw = packet.getYaw();
+ to.getLoc().pitch = packet.getPitch();
+ }
+ to.setBox(new SimpleCollisionBox(to.getLoc(), 0.6, 1.8));
+ to.setOnGround(packet.isOnGround());
+ }
+
+ /**
+ * If from location is null, update to loc after to is set, otherwise, update to before from.
+ * Updates the location of player and its general delta movement.
+ * @param packet WPacketPlayInFlying
+ */
+ private void updateLocations(WPacketPlayInFlying packet) {
+ from.setLoc(to);
+ setTo(packet);
+
+ lDeltaX = deltaX;
+ lDeltaY = deltaY;
+ lDeltaZ = deltaZ;
+ lDeltaXZ = deltaXZ;
+ lDeltaYaw = deltaYaw;
+ lDeltaPitch = deltaPitch;
+
+ deltaX = to.getLoc().x - from.getLoc().x;
+ deltaY = to.getLoc().y - from.getLoc().y;
+ deltaZ = to.getLoc().z - from.getLoc().z;
+ deltaXZ = Math.hypot(deltaX, deltaZ); // Calculating here to cache since hypot() can be heavy.
+ deltaYaw = MathUtils.getAngleDelta(to.getLoc().yaw, from.getLoc().yaw);
+ deltaPitch = to.getLoc().pitch - from.getLoc().pitch;
+ }
+}
diff --git a/src/main/java/dev/brighten/ac/data/obj/CMove.java b/src/main/java/dev/brighten/ac/data/obj/CMove.java
new file mode 100644
index 0000000..8ae822e
--- /dev/null
+++ b/src/main/java/dev/brighten/ac/data/obj/CMove.java
@@ -0,0 +1,22 @@
+package dev.brighten.ac.data.obj;
+
+import dev.brighten.ac.utils.KLocation;
+import dev.brighten.ac.utils.world.types.SimpleCollisionBox;
+import lombok.Getter;
+import lombok.Setter;
+import org.bukkit.World;
+
+@Getter
+@Setter
+public class CMove {
+ private KLocation loc = new KLocation(0,0,0,0,0);
+ private World world;
+ private SimpleCollisionBox box;
+ private boolean onGround;
+ public void setLoc(CMove move) {
+ this.loc = move.getLoc().clone();
+ this.world = move.getWorld();
+ this.box = move.getBox();
+ this.onGround = move.isOnGround();
+ }
+}
diff --git a/src/main/java/dev/brighten/ac/data/obj/InstantAction.java b/src/main/java/dev/brighten/ac/data/obj/InstantAction.java
new file mode 100644
index 0000000..10da398
--- /dev/null
+++ b/src/main/java/dev/brighten/ac/data/obj/InstantAction.java
@@ -0,0 +1,13 @@
+package dev.brighten.ac.data.obj;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+@RequiredArgsConstructor
+@Getter
+public class InstantAction {
+ private final short startId, endId;
+ private final boolean end;
+ private final long stamp = System.currentTimeMillis();
+
+}
diff --git a/src/main/java/dev/brighten/ac/data/obj/NormalAction.java b/src/main/java/dev/brighten/ac/data/obj/NormalAction.java
new file mode 100644
index 0000000..5ef6b01
--- /dev/null
+++ b/src/main/java/dev/brighten/ac/data/obj/NormalAction.java
@@ -0,0 +1,12 @@
+package dev.brighten.ac.data.obj;
+
+import dev.brighten.ac.handler.keepalive.KeepAlive;
+import lombok.AllArgsConstructor;
+
+import java.util.function.Consumer;
+
+@AllArgsConstructor
+public class NormalAction {
+ public int stamp;
+ public Consumer action;
+}
\ No newline at end of file
diff --git a/src/main/java/dev/brighten/ac/handler/PacketHandler.java b/src/main/java/dev/brighten/ac/handler/PacketHandler.java
new file mode 100644
index 0000000..95a4347
--- /dev/null
+++ b/src/main/java/dev/brighten/ac/handler/PacketHandler.java
@@ -0,0 +1,94 @@
+package dev.brighten.ac.handler;
+
+import dev.brighten.ac.Anticheat;
+import dev.brighten.ac.data.APlayer;
+import dev.brighten.ac.data.obj.NormalAction;
+import dev.brighten.ac.packet.wrapper.WPacket;
+import dev.brighten.ac.packet.wrapper.in.WPacketPlayInFlying;
+import dev.brighten.ac.packet.wrapper.out.WPacketPlayOutEntityEffect;
+import lombok.val;
+import net.minecraft.server.v1_8_R3.Packet;
+import net.minecraft.server.v1_8_R3.PacketPlayInTransaction;
+
+import java.util.Deque;
+import java.util.LinkedList;
+import java.util.Optional;
+
+public class PacketHandler {
+ public void process(APlayer player, Packet packetObject) {
+ if(packetObject instanceof PacketPlayInTransaction) {
+ long currentTimeMillis = System.currentTimeMillis();
+ PacketPlayInTransaction packet = (PacketPlayInTransaction) packetObject;
+
+ if(packet.a() == 0) {
+ if(Anticheat.INSTANCE.getKeepaliveProcessor().keepAlives.getIfPresent(packet.b()) != null) {
+ Anticheat.INSTANCE.getKeepaliveProcessor().addResponse(player, packet.b());
+
+ val optional = Anticheat.INSTANCE.getKeepaliveProcessor().getResponse(player);
+
+ int current = Anticheat.INSTANCE.getKeepaliveProcessor().tick;
+
+ optional.ifPresent(ka -> {
+ player.addPlayerTick();
+
+ player.getLagInfo().setLastTransPing(player.getLagInfo().getTransPing());
+ player.getLagInfo().setTransPing(current - ka.start);
+
+ if(player.instantTransaction.size() > 0) {
+ synchronized (player.instantTransaction) {
+ Deque toRemove = new LinkedList<>();
+ player.instantTransaction.forEach((key, tuple) -> {
+ if((currentTimeMillis - tuple.one.getStamp())
+ > player.getLagInfo().getTransPing() * 52L + 750L) {
+ tuple.two.accept(tuple.one);
+ toRemove.add(key);
+ }
+ });
+ Short key = null;
+ while((key = toRemove.poll()) != null) {
+ player.instantTransaction.remove(key);
+ }
+ }
+ }
+
+ if(Math.abs(player.getLagInfo().getLastTransPing() - player.getLagInfo().getTransPing()) > 1) {
+ player.getLagInfo().getLastPingDrop().reset();
+ }
+
+ ka.getReceived(player.getBukkitPlayer().getUniqueId()).ifPresent(r -> {
+ r.receivedStamp = currentTimeMillis;
+ });
+
+ for (NormalAction action : player.keepAliveStamps) {
+ if(action.stamp > ka.start) continue;
+
+ action.action.accept(ka);
+ player.keepAliveStamps.remove(action);
+ }
+ });
+ player.getLagInfo().getLastClientTransaction().reset();
+ } else {
+ Optional.ofNullable(player.instantTransaction.remove(packet.b()))
+ .ifPresent(t -> t.two.accept(t.one));
+ }
+ }
+ }
+
+ player.callPacket(packetObject);
+ }
+
+ public void process(APlayer player, WPacket packetObject) {
+
+ if(packetObject instanceof WPacketPlayInFlying) {
+ WPacketPlayInFlying packet = (WPacketPlayInFlying) packetObject;
+
+ player.getMovement().process(packet, System.currentTimeMillis());
+ } else if(packetObject instanceof WPacketPlayOutEntityEffect) {
+ WPacketPlayOutEntityEffect packet = (WPacketPlayOutEntityEffect) packetObject;
+
+ player.getPotionHandler().onPotionEffect(packet);
+ }
+
+ player.callPacket(packetObject);
+ }
+}
diff --git a/src/main/java/dev/brighten/ac/handler/PotionHandler.java b/src/main/java/dev/brighten/ac/handler/PotionHandler.java
new file mode 100644
index 0000000..a26eafa
--- /dev/null
+++ b/src/main/java/dev/brighten/ac/handler/PotionHandler.java
@@ -0,0 +1,55 @@
+package dev.brighten.ac.handler;
+
+import dev.brighten.ac.data.APlayer;
+import dev.brighten.ac.packet.wrapper.in.WPacketPlayInFlying;
+import dev.brighten.ac.packet.wrapper.out.WPacketPlayOutEntityEffect;
+import lombok.RequiredArgsConstructor;
+import lombok.val;
+import org.bukkit.potion.PotionEffect;
+import org.bukkit.potion.PotionEffectType;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+@RequiredArgsConstructor
+public class PotionHandler {
+ private final APlayer data;
+
+ public List potionEffects = new CopyOnWriteArrayList<>();
+
+ public void onFlying(WPacketPlayInFlying packet) {
+ for (PotionEffect effect : potionEffects) {
+ if(data.getBukkitPlayer().hasPotionEffect(effect.getType())) continue;
+
+ data.runKeepaliveAction(d -> data.getPotionHandler().potionEffects.remove(effect));
+ }
+ }
+
+ public void onPotionEffect(WPacketPlayOutEntityEffect packet) {
+ data.runKeepaliveAction(d -> {
+ val type = PotionEffectType.getById(packet.getEffectId());
+ data.getPotionHandler().potionEffects.stream().filter(pe -> pe.getType().equals(type))
+ .forEach(data.getPotionHandler().potionEffects::remove);
+ data.getPotionHandler().potionEffects
+ .add(new PotionEffect(type, packet.getDuration(), packet.getAmplifier(),
+ (packet.getFlags() & 1) == 1));
+ });
+ }
+
+ public boolean hasPotionEffect(PotionEffectType type) {
+ for (PotionEffect potionEffect : potionEffects) {
+ if(potionEffect.getType().equals(type))
+ return true;
+ }
+ return false;
+ }
+
+ public Optional getEffectByType(PotionEffectType type) {
+ for (PotionEffect potionEffect : potionEffects) {
+ if(potionEffect.getType().equals(type))
+ return Optional.of(potionEffect);
+ }
+ return Optional.empty();
+ }
+}
diff --git a/src/main/java/dev/brighten/ac/handler/keepalive/KeepAlive.java b/src/main/java/dev/brighten/ac/handler/keepalive/KeepAlive.java
new file mode 100644
index 0000000..fb7b5a5
--- /dev/null
+++ b/src/main/java/dev/brighten/ac/handler/keepalive/KeepAlive.java
@@ -0,0 +1,40 @@
+package dev.brighten.ac.handler.keepalive;
+
+import dev.brighten.ac.Anticheat;
+import dev.brighten.ac.data.APlayer;
+import lombok.RequiredArgsConstructor;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.UUID;
+
+public class KeepAlive {
+
+ public final int start;
+ public final short id;
+ public long startStamp;
+
+ public KeepAlive(int start, short id) {
+ this.start = start;
+ this.id = id;
+ }
+
+ public final Map receivedKeepalive = new HashMap<>();
+
+ public void received(APlayer player) {
+ receivedKeepalive.put(player.getBukkitPlayer().getUniqueId(),
+ new KAReceived(player, Anticheat.INSTANCE.getKeepaliveProcessor().tick));
+ }
+
+ public Optional getReceived(UUID uuid) {
+ return Optional.ofNullable(receivedKeepalive.getOrDefault(uuid, null));
+ }
+
+ @RequiredArgsConstructor
+ public static class KAReceived {
+ public final APlayer data;
+ public final int stamp;
+ public long receivedStamp;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/dev/brighten/ac/handler/keepalive/KeepaliveProcessor.java b/src/main/java/dev/brighten/ac/handler/keepalive/KeepaliveProcessor.java
new file mode 100644
index 0000000..8c99df1
--- /dev/null
+++ b/src/main/java/dev/brighten/ac/handler/keepalive/KeepaliveProcessor.java
@@ -0,0 +1,100 @@
+package dev.brighten.ac.handler.keepalive;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import dev.brighten.ac.Anticheat;
+import dev.brighten.ac.data.APlayer;
+import dev.brighten.ac.packet.handler.HandlerAbstract;
+import dev.brighten.ac.utils.RunUtils;
+import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
+import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
+import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
+import net.minecraft.server.v1_8_R3.PacketPlayOutTransaction;
+import org.bukkit.scheduler.BukkitTask;
+
+import java.util.Optional;
+import java.util.concurrent.TimeUnit;
+
+public class KeepaliveProcessor implements Runnable {
+
+ private BukkitTask task;
+
+ public KeepAlive currentKeepalive = new KeepAlive(0, (short)0);
+ public int tick;
+ public int totalPlayers, laggyPlayers;
+
+ public final Cache keepAlives = CacheBuilder.newBuilder().concurrencyLevel(4)
+ .expireAfterWrite(15, TimeUnit.SECONDS).build();
+
+ final Int2ObjectMap lastResponses = Int2ObjectMaps.synchronize(new Int2ObjectOpenHashMap<>());
+
+ public KeepaliveProcessor() {
+ start();
+ }
+
+ @Override
+ public void run() {
+ tick++;
+ synchronized (keepAlives) {
+ short id = (short) (tick > Short.MAX_VALUE ? tick % Short.MAX_VALUE : tick);
+
+ //Ensuring we don't have any duplicate IDS
+
+ currentKeepalive = new KeepAlive(tick, id);
+ keepAlives.put(currentKeepalive.id, currentKeepalive);
+ }
+
+ currentKeepalive.startStamp = System.currentTimeMillis();
+ totalPlayers = laggyPlayers = 0;
+ for (APlayer value : Anticheat.INSTANCE.getPlayerRegistry().aplayerMap.values()) {
+ totalPlayers++;
+
+ if(value.getLagInfo().getLastPingDrop().isNotPassed(2)
+ || value.getLagInfo().getLastClientTransaction().isPassed(135L)) laggyPlayers++;
+
+ if(value.getInfo().getTarget() != null) {
+ value.getInfo().getTargetPastLocation().addLocation(value.getInfo()
+ .getTarget().getLocation());
+ }
+
+ PacketPlayOutTransaction transaction = new PacketPlayOutTransaction(0, currentKeepalive.id, false);
+
+ HandlerAbstract.getHandler().sendPacket(value.getBukkitPlayer(), transaction);
+ }
+ }
+
+ public Optional getKeepByTick(int tick) {
+ return keepAlives.asMap().values().stream().filter(ka -> ka.start == tick).findFirst();
+ }
+
+ public Optional getKeepById(short id) {
+ return Optional.ofNullable(keepAlives.getIfPresent(id));
+ }
+
+ public Optional getResponse(APlayer data) {
+ if(!lastResponses.containsKey(data.getBukkitPlayer().getUniqueId().hashCode()))
+ return Optional.empty();
+
+ return getKeepById(lastResponses.get(data.getBukkitPlayer().getUniqueId().hashCode()));
+ }
+
+ public void start() {
+ if(task == null) {
+ task = RunUtils.taskTimer(this, Anticheat.INSTANCE, 20L, 0L);
+ }
+ }
+
+ public void addResponse(APlayer data, short id) {
+ getKeepById(id).ifPresent(ka -> {
+ lastResponses.put(data.getBukkitPlayer().getUniqueId().hashCode(), (Short)id);
+ ka.received(data);
+ });
+ }
+
+ public void stop() {
+ if(task != null) {
+ task.cancel();
+ task = null;
+ }
+ }
+}
diff --git a/src/main/java/dev/brighten/ac/listener/JoinListener.java b/src/main/java/dev/brighten/ac/listener/JoinListener.java
index 8178efd..91cb97b 100644
--- a/src/main/java/dev/brighten/ac/listener/JoinListener.java
+++ b/src/main/java/dev/brighten/ac/listener/JoinListener.java
@@ -1,35 +1,48 @@
package dev.brighten.ac.listener;
import dev.brighten.ac.Anticheat;
+import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.handler.HandlerAbstract;
-import dev.brighten.ac.packet.wrapper.PacketType;
+import dev.brighten.ac.packet.wrapper.WPacket;
import dev.brighten.ac.utils.Init;
import dev.brighten.ac.utils.RunUtils;
+import net.minecraft.server.v1_8_R3.Packet;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
+import java.util.Optional;
+
@Init
public class JoinListener implements Listener {
public JoinListener() {
Anticheat.INSTANCE.getPacketProcessor().processAsync(Anticheat.INSTANCE, EventPriority.NORMAL, event -> {
- if(event.getType().equals(PacketType.BLOCK_DIG)) {
+ Optional aplayer = Anticheat.INSTANCE.getPlayerRegistry()
+ .getPlayer(event.getPlayer().getUniqueId());
- event.getPlayer().sendMessage("BlockDig:");
- }
+ aplayer.ifPresent(player -> {
+ if(event.getPacket() instanceof WPacket) {
+ Anticheat.INSTANCE.getPacketHandler().process(player, (WPacket) event.getPacket());
+ } else {
+ Anticheat.INSTANCE.getPacketHandler().process(player, (Packet) event.getPacket());
+ }
+ });
});
}
@EventHandler
public void onJoin(PlayerJoinEvent event) {
+ APlayer player = Anticheat.INSTANCE.getPlayerRegistry().generate(event.getPlayer());
RunUtils.taskLater(() -> HandlerAbstract.getHandler().add(event.getPlayer()), 4L);
+
+ player.callEvent(event);
}
@EventHandler
- public void onLeave(PlayerQuitEvent event) {
- HandlerAbstract.getHandler().remove(event.getPlayer());
+ public void onQuit(PlayerQuitEvent event) {
+ Anticheat.INSTANCE.getPlayerRegistry().unregister(event.getPlayer().getUniqueId());
}
}
diff --git a/src/main/java/dev/brighten/ac/packet/handler/HandlerAbstract.java b/src/main/java/dev/brighten/ac/packet/handler/HandlerAbstract.java
index 8306da4..c852d25 100644
--- a/src/main/java/dev/brighten/ac/packet/handler/HandlerAbstract.java
+++ b/src/main/java/dev/brighten/ac/packet/handler/HandlerAbstract.java
@@ -1,5 +1,6 @@
package dev.brighten.ac.packet.handler;
+import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.ProtocolVersion;
import dev.brighten.ac.utils.reflections.Reflections;
import dev.brighten.ac.utils.reflections.types.WrappedClass;
@@ -14,7 +15,7 @@ public abstract class HandlerAbstract {
fieldNetworkManager = Reflections.getNMSClass("PlayerConnection").getFieldByName("networkManager"),
fieldPlayerConnection = Reflections.getNMSClass("EntityPlayer").getFieldByName("playerConnection");
- static String handlerName = "brigten-ac-packets";
+ static String handlerName = "brighten-ac-packets";
@Getter
private static HandlerAbstract handler;
@@ -30,4 +31,8 @@ public abstract class HandlerAbstract {
public abstract void add(Player player);
public abstract void remove(Player player);
+
+ public abstract void sendPacket(Player player, Object packet);
+
+ public abstract void sendPacket(APlayer player, Object packet);
}
diff --git a/src/main/java/dev/brighten/ac/packet/handler/LegacyHandler.java b/src/main/java/dev/brighten/ac/packet/handler/LegacyHandler.java
index 56d1b0e..e4fc0e9 100644
--- a/src/main/java/dev/brighten/ac/packet/handler/LegacyHandler.java
+++ b/src/main/java/dev/brighten/ac/packet/handler/LegacyHandler.java
@@ -1,6 +1,7 @@
package dev.brighten.ac.packet.handler;
import dev.brighten.ac.Anticheat;
+import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.wrapper.PacketType;
import dev.brighten.ac.utils.reflections.types.WrappedClass;
import dev.brighten.ac.utils.reflections.types.WrappedField;
@@ -58,6 +59,16 @@ public class LegacyHandler extends HandlerAbstract {
channel.eventLoop().execute(() -> channel.pipeline().remove(handlerName));
}
+ @Override
+ public void sendPacket(Player player, Object packet) {
+ getChannel(player).pipeline().writeAndFlush(packet);
+ }
+
+ @Override
+ public void sendPacket(APlayer player, Object packet) {
+ sendPacket(player.getBukkitPlayer(), packet);
+ }
+
private Channel getChannel(Player player) {
synchronized (channelCache) {
return channelCache.computeIfAbsent(player.getName(), name -> {
diff --git a/src/main/java/dev/brighten/ac/packet/handler/ModernHandler.java b/src/main/java/dev/brighten/ac/packet/handler/ModernHandler.java
index b15dfe6..511964a 100644
--- a/src/main/java/dev/brighten/ac/packet/handler/ModernHandler.java
+++ b/src/main/java/dev/brighten/ac/packet/handler/ModernHandler.java
@@ -2,6 +2,7 @@ package dev.brighten.ac.packet.handler;
import com.google.common.collect.MapMaker;
import dev.brighten.ac.Anticheat;
+import dev.brighten.ac.data.APlayer;
import dev.brighten.ac.packet.wrapper.PacketType;
import dev.brighten.ac.utils.reflections.Reflections;
import dev.brighten.ac.utils.reflections.types.WrappedField;
@@ -58,6 +59,16 @@ public class ModernHandler extends HandlerAbstract {
channel.eventLoop().execute(() -> channel.pipeline().remove(handlerName));
}
+ @Override
+ public void sendPacket(Player player, Object packet) {
+ getChannel(player).pipeline().writeAndFlush(packet);
+ }
+
+ @Override
+ public void sendPacket(APlayer player, Object packet) {
+ sendPacket(player.getBukkitPlayer(), packet);
+ }
+
private Channel getChannel(Player player) {
synchronized (channelCache) {
return channelCache.computeIfAbsent(player.getName(), name -> {
@@ -78,8 +89,11 @@ public class ModernHandler extends HandlerAbstract {
int index = name.lastIndexOf(".");
String packetName = name.substring(index + 1);
- boolean allowed = Anticheat.INSTANCE.getPacketProcessor().call(player, msg, PacketType
- .getByPacketId(packetName).orElse(PacketType.NONE));
+ PacketType type = PacketType
+ .getByPacketId(packetName).orElse(PacketType.NONE);
+
+ boolean allowed = Anticheat.INSTANCE.getPacketProcessor().call(player, PacketType.processType(type, msg),
+ type);
if(allowed) {
super.channelRead(ctx, msg);
diff --git a/src/main/java/dev/brighten/ac/packet/listener/PacketInfo.java b/src/main/java/dev/brighten/ac/packet/listener/PacketInfo.java
index 4768e98..e53d72e 100644
--- a/src/main/java/dev/brighten/ac/packet/listener/PacketInfo.java
+++ b/src/main/java/dev/brighten/ac/packet/listener/PacketInfo.java
@@ -7,12 +7,20 @@ import lombok.Setter;
import org.bukkit.entity.Player;
@RequiredArgsConstructor
-@Getter
public class PacketInfo {
+ @Getter
private final Player player;
private final Object packet;
+ @Getter
private final PacketType type;
+ @Getter
private final long timestamp;
+ @Getter
@Setter
private boolean cancelled;
+
+ public T getPacket() {
+ return (T) packet;
+ }
+
}
diff --git a/src/main/java/dev/brighten/ac/packet/listener/PacketProcessor.java b/src/main/java/dev/brighten/ac/packet/listener/PacketProcessor.java
index 1564944..463f50a 100644
--- a/src/main/java/dev/brighten/ac/packet/listener/PacketProcessor.java
+++ b/src/main/java/dev/brighten/ac/packet/listener/PacketProcessor.java
@@ -2,7 +2,6 @@ package dev.brighten.ac.packet.listener;
import dev.brighten.ac.Anticheat;
import dev.brighten.ac.packet.ProtocolVersion;
-import dev.brighten.ac.packet.handler.HandlerAbstract;
import dev.brighten.ac.packet.listener.functions.PacketListener;
import dev.brighten.ac.packet.wrapper.PacketConverter;
import dev.brighten.ac.packet.wrapper.PacketType;
@@ -11,7 +10,6 @@ import dev.brighten.ac.utils.MiscUtils;
import dev.brighten.ac.utils.RunUtils;
import lombok.Getter;
import lombok.val;
-import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventPriority;
import org.bukkit.plugin.Plugin;
@@ -184,6 +182,5 @@ public class PacketProcessor {
public void shutdown() {
processors.clear();
asyncProcessors.clear();
- Bukkit.getOnlinePlayers().forEach(HandlerAbstract.getHandler()::remove);
}
}
diff --git a/src/main/java/dev/brighten/ac/packet/packet/WPacket.java b/src/main/java/dev/brighten/ac/packet/packet/WPacket.java
deleted file mode 100644
index 61fab5b..0000000
--- a/src/main/java/dev/brighten/ac/packet/packet/WPacket.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package dev.brighten.ac.packet.packet;
-
-public interface WPacket {
-}
diff --git a/src/main/java/dev/brighten/ac/packet/wrapper/PacketConverter.java b/src/main/java/dev/brighten/ac/packet/wrapper/PacketConverter.java
index f125533..89929a0 100644
--- a/src/main/java/dev/brighten/ac/packet/wrapper/PacketConverter.java
+++ b/src/main/java/dev/brighten/ac/packet/wrapper/PacketConverter.java
@@ -1,6 +1,7 @@
package dev.brighten.ac.packet.wrapper;
import dev.brighten.ac.packet.wrapper.in.*;
+import dev.brighten.ac.packet.wrapper.out.WPacketPlayOutEntityEffect;
public interface PacketConverter {
WPacketPlayInFlying processFlying(Object object);
@@ -15,4 +16,8 @@ public interface PacketConverter {
WPacketPlayInBlockPlace processBlockPlace(Object object);
WPacketPlayInCloseWindow processCloseWindow(Object object);
+
+ WPacketPlayInEntityAction processEntityAction(Object object);
+
+ WPacketPlayOutEntityEffect processEntityEffect(Object object);
}
diff --git a/src/main/java/dev/brighten/ac/packet/wrapper/PacketType.java b/src/main/java/dev/brighten/ac/packet/wrapper/PacketType.java
index b29b1fd..21b6b84 100644
--- a/src/main/java/dev/brighten/ac/packet/wrapper/PacketType.java
+++ b/src/main/java/dev/brighten/ac/packet/wrapper/PacketType.java
@@ -1,5 +1,6 @@
package dev.brighten.ac.packet.wrapper;
+import dev.brighten.ac.Anticheat;
import lombok.Getter;
import java.util.Optional;
@@ -26,6 +27,10 @@ public enum PacketType {
CLOSE_WINDOW("PacketPlayInCloseWindow"),
+ ENTITY_ACTION("PacketPlayInEntityAction"),
+
+ ENTITY_EFFECT("PacketPlayOutEntityEffect"),
+
NONE();
PacketType(String... packetIds) {
@@ -92,5 +97,36 @@ public enum PacketType {
public static final String STATUS_START = "PacketStatusInStart";
public static final String LOGIN_START = "PacketLoginInStart";
}
+
+ public static Object processType(PacketType type, Object object) {
+ PacketConverter convert = Anticheat.INSTANCE.getPacketProcessor().getPacketConverter();
+
+ switch (type) {
+ case FLYING: {
+ return convert.processFlying(object);
+ }
+ case CHAT:
+ break;
+ case BLOCK_DIG:
+ return convert.processBlockDig(object);
+ case USE_ENTITY:
+ return convert.processUseEntity(object);
+ case BLOCK_PLACE:
+ return convert.processBlockPlace(object);
+ case CLOSE_WINDOW:
+ return convert.processCloseWindow(object);
+ case ARM_ANIMATION:
+ return convert.processAnimation(object);
+ case ENTITY_ACTION:
+ return convert.processEntityAction(object);
+ case CLIENT_ABILITIES:
+ return convert.processAbilities(object);
+ case ENTITY_EFFECT:
+ return convert.processEntityEffect(object);
+ default:
+ return object;
+ }
+ return object;
+ }
}
diff --git a/src/main/java/dev/brighten/ac/packet/wrapper/impl/Processor_18.java b/src/main/java/dev/brighten/ac/packet/wrapper/impl/Processor_18.java
index ffd7261..ff2aa9d 100644
--- a/src/main/java/dev/brighten/ac/packet/wrapper/impl/Processor_18.java
+++ b/src/main/java/dev/brighten/ac/packet/wrapper/impl/Processor_18.java
@@ -3,6 +3,7 @@ package dev.brighten.ac.packet.wrapper.impl;
import dev.brighten.ac.packet.wrapper.PacketConverter;
import dev.brighten.ac.packet.wrapper.in.*;
import dev.brighten.ac.packet.wrapper.objects.WrappedEnumDirection;
+import dev.brighten.ac.packet.wrapper.out.WPacketPlayOutEntityEffect;
import dev.brighten.ac.utils.math.IntVector;
import dev.brighten.ac.utils.reflections.types.WrappedClass;
import dev.brighten.ac.utils.reflections.types.WrappedField;
@@ -67,7 +68,7 @@ public class Processor_18 implements PacketConverter {
BlockPosition pos = packet.a();
return WPacketPlayInBlockPlace.builder().blockPos(new IntVector(pos.getX(), pos.getY(), pos.getZ()))
- .direction(WrappedEnumDirection.values()[packet.getFace()])
+ .direction(WrappedEnumDirection.values()[Math.min(packet.getFace(), 5)])
.itemStack(CraftItemStack.asCraftMirror(packet.getItemStack()))
.vecX(packet.d()).vecY(packet.e()).vecZ(packet.f())
.build();
@@ -75,11 +76,39 @@ public class Processor_18 implements PacketConverter {
private static final WrappedClass classCloseWindow = new WrappedClass(PacketPlayInCloseWindow.class);
- private static final WrappedField fieldWindowId = classAbilities.getFieldByType(int.class, 0);
+ private static final WrappedField fieldWindowId = classCloseWindow.getFieldByType(int.class, 0);
@Override
public WPacketPlayInCloseWindow processCloseWindow(Object object) {
PacketPlayInCloseWindow packet = (PacketPlayInCloseWindow) object;
return WPacketPlayInCloseWindow.builder().id(fieldWindowId.get(packet)).build();
}
+
+ @Override
+ public WPacketPlayInEntityAction processEntityAction(Object object) {
+ PacketPlayInEntityAction packet = (PacketPlayInEntityAction) object;
+
+ return WPacketPlayInEntityAction.builder().action(WPacketPlayInEntityAction.EnumPlayerAction
+ .valueOf(packet.b().name())).build();
+ }
+
+ private static final WrappedClass classEntityEffect = new WrappedClass(PacketPlayOutEntityEffect.class);
+ private static final WrappedField fieldEntityId2, fieldEffectId, fieldAmplifier, fieldDuration, fieldFlags;
+
+ static {
+ fieldEntityId2 = classEntityEffect.getFieldByType(Integer.TYPE, 0);
+ fieldEffectId = classEntityEffect.getFieldByType(Byte.TYPE, 0);
+ fieldAmplifier = classEntityEffect.getFieldByType(Byte.TYPE, 1);
+ fieldDuration = classEntityEffect.getFieldByType(Integer.TYPE, 1);
+ fieldFlags = classEntityEffect.getFieldByType(Byte.TYPE, 2);
+ }
+
+ @Override
+ public WPacketPlayOutEntityEffect processEntityEffect(Object object) {
+ return WPacketPlayOutEntityEffect.builder().effectId(fieldEffectId.get(object))
+ .amplifier(fieldAmplifier.get(object))
+ .flags(fieldFlags.get(object))
+ .entityId(fieldEntityId2.get(object))
+ .duration(fieldDuration.get(object)).build();
+ }
}
diff --git a/src/main/java/dev/brighten/ac/packet/wrapper/in/WPacketPlayInEntityAction.java b/src/main/java/dev/brighten/ac/packet/wrapper/in/WPacketPlayInEntityAction.java
new file mode 100644
index 0000000..3351a77
--- /dev/null
+++ b/src/main/java/dev/brighten/ac/packet/wrapper/in/WPacketPlayInEntityAction.java
@@ -0,0 +1,31 @@
+package dev.brighten.ac.packet.wrapper.in;
+
+import dev.brighten.ac.packet.wrapper.PacketType;
+import dev.brighten.ac.packet.wrapper.WPacket;
+import lombok.Builder;
+import lombok.Getter;
+
+@Builder
+@Getter
+public class WPacketPlayInEntityAction implements WPacket {
+
+ private EnumPlayerAction action;
+
+ @Override
+ public PacketType getPacketType() {
+ return PacketType.ENTITY_ACTION;
+ }
+
+ public static enum EnumPlayerAction {
+ START_SNEAKING,
+ STOP_SNEAKING,
+ STOP_SLEEPING,
+ START_SPRINTING,
+ STOP_SPRINTING,
+ RIDING_JUMP,
+ OPEN_INVENTORY;
+
+ private EnumPlayerAction() {
+ }
+ }
+}
diff --git a/src/main/java/dev/brighten/ac/packet/wrapper/in/WPacketPlayInUseEntity.java b/src/main/java/dev/brighten/ac/packet/wrapper/in/WPacketPlayInUseEntity.java
index e2bcef2..2623e69 100644
--- a/src/main/java/dev/brighten/ac/packet/wrapper/in/WPacketPlayInUseEntity.java
+++ b/src/main/java/dev/brighten/ac/packet/wrapper/in/WPacketPlayInUseEntity.java
@@ -21,7 +21,6 @@ public class WPacketPlayInUseEntity implements WPacket {
return PacketType.USE_ENTITY;
}
-
public enum EnumHand {
MAIN_HAND, OFF_HAND;
}
diff --git a/src/main/java/dev/brighten/ac/utils/ClassScanner.java b/src/main/java/dev/brighten/ac/utils/ClassScanner.java
index ab3f5ab..6b66efc 100644
--- a/src/main/java/dev/brighten/ac/utils/ClassScanner.java
+++ b/src/main/java/dev/brighten/ac/utils/ClassScanner.java
@@ -1,5 +1,8 @@
package dev.brighten.ac.utils;
+import dev.brighten.ac.Anticheat;
+import dev.brighten.ac.utils.reflections.Reflections;
+import dev.brighten.ac.utils.reflections.types.WrappedClass;
import org.bukkit.Bukkit;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.tree.AnnotationNode;
@@ -8,6 +11,7 @@ import org.objectweb.asm.tree.ClassNode;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
+import java.lang.annotation.Annotation;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
@@ -18,6 +22,7 @@ import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
+import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
@@ -39,6 +44,125 @@ public class ClassScanner {
return scanFile(file, urls);
}
+ public static Set getClasses(Class extends Annotation> annotationClass,
+ String packageName) {
+ return scanFile(annotationClass).stream().filter(pkg -> pkg.startsWith(packageName))
+ .map(Reflections::getClass).collect(Collectors.toSet());
+ }
+
+ public static Set getClasses(Class extends Annotation> annotationClass) {
+ return scanFile(annotationClass).stream().map(Reflections::getClass).collect(Collectors.toSet());
+ }
+
+ public static Set scanFile(Class extends Annotation> annotationClass) {
+ return scanFile(annotationClass, new URL[]{Anticheat.class.getProtectionDomain().getCodeSource().getLocation()});
+ }
+
+ public static Set scanFile(Class extends Annotation> annotationClass, URL[] urls) {
+ Set sources = new HashSet<>();
+ Set plugins = new HashSet<>();
+
+
+ for (URL url : urls) {
+ if (!url.getProtocol().equals("file")) {
+ continue;
+ }
+
+ URI source;
+ try {
+ source = url.toURI();
+ } catch (URISyntaxException e) {
+ continue;
+ }
+
+ if (sources.add(source)) {
+ scanPath(Paths.get(source), annotationClass, plugins);
+ }
+ }
+
+ return plugins;
+ }
+
+ private static void scanPath(Path path, Class extends Annotation> annotationClass, Set plugins) {
+ if (Files.exists(path)) {
+ if (Files.isDirectory(path)) {
+ scanDirectory(path, annotationClass, plugins);
+ } else {
+ scanZip(path, annotationClass, plugins);
+ }
+ }
+ }
+
+ private static void scanDirectory(Path dir, Class extends Annotation> annotationClass, final Set plugins) {
+ try {
+ Files.walkFileTree(dir, newHashSet(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE,
+ new SimpleFileVisitor() {
+ @Override
+ public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException {
+ if (CLASS_FILE.matches(path.getFileName())) {
+ try (InputStream in = Files.newInputStream(path)) {
+ String plugin = findClass(in, annotationClass);
+ if (plugin != null) {
+ plugins.add(plugin);
+ }
+ }
+ }
+
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+
+ public static String findClass(InputStream in, Class extends Annotation> annotationClass) {
+ try {
+ ClassReader reader = new ClassReader(in);
+ ClassNode classNode = new ClassNode();
+ reader.accept(classNode, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
+ String className = classNode.name.replace('/', '.');
+ final String anName = annotationClass.getName().replace(".", "/");
+ if (classNode.visibleAnnotations != null) {
+ for (Object node : classNode.visibleAnnotations) {
+ AnnotationNode annotation = (AnnotationNode) node;
+ if (annotation.desc
+ .equals("L" + anName + ";"))
+ return className;
+ }
+ }
+ } catch (Exception e) {
+ //Bukkit.getLogger().info("Failed to scan: " + in.toString());
+ }
+ return null;
+ }
+
+ private static void scanZip(Path path, Class extends Annotation> annotationClass, Set plugins) {
+ if (!ARCHIVE.matches(path.getFileName())) {
+ return;
+ }
+
+ try (ZipFile zip = new ZipFile(path.toFile())) {
+ Enumeration extends ZipEntry> entries = zip.entries();
+ while (entries.hasMoreElements()) {
+ ZipEntry entry = entries.nextElement();
+ if (entry.isDirectory() || !entry.getName().endsWith(".class")) {
+ continue;
+ }
+
+ try (InputStream in = zip.getInputStream(entry)) {
+ String plugin = findClass(in, annotationClass);
+ if (plugin != null) {
+ plugins.add(plugin);
+ }
+ }
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
public static Set scanFile(String file, Class> clazz) {
return scanFile(file, new URL[]{clazz.getProtectionDomain().getCodeSource().getLocation()});
}
diff --git a/src/main/java/dev/brighten/ac/utils/CollisionHandler.java b/src/main/java/dev/brighten/ac/utils/CollisionHandler.java
new file mode 100644
index 0000000..96c8a97
--- /dev/null
+++ b/src/main/java/dev/brighten/ac/utils/CollisionHandler.java
@@ -0,0 +1,215 @@
+package dev.brighten.ac.utils;
+
+import dev.brighten.ac.data.APlayer;
+import dev.brighten.ac.packet.ProtocolVersion;
+import dev.brighten.ac.utils.world.BlockData;
+import dev.brighten.ac.utils.world.CollisionBox;
+import dev.brighten.ac.utils.world.EntityData;
+import dev.brighten.ac.utils.world.types.SimpleCollisionBox;
+import lombok.Getter;
+import lombok.Setter;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.block.Block;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.EntityType;
+
+import java.util.*;
+import java.util.function.Consumer;
+
+@Getter
+public class
+CollisionHandler {
+ private List blocks;
+ private List entities;
+ private APlayer data;
+ private KLocation location;
+ private List>> intersects = new ArrayList<>(),
+ collides = new ArrayList<>();
+
+ private double width, height;
+ private double shift;
+ @Setter
+ private boolean single = false;
+ @Setter
+ private boolean debugging;
+
+ public CollisionHandler(List blocks, Collection entities, KLocation to, APlayer data) {
+ this.blocks = blocks;
+ this.entities = new ArrayList<>(entities);
+ this.location = to;
+ this.data = data;
+ }
+
+ public void setSize(double width, double height) {
+ this.width = width;
+ this.height = height;
+ }
+
+ public void setOffset(double shift) {
+ this.shift = shift;
+ }
+
+ public boolean containsFlag(int bitmask) {
+ for (Block b : blocks) {
+ if (Materials.checkFlag(b.getType(), bitmask)) return true;
+ }
+ return false;
+ }
+
+ public boolean contains(EntityType type) {
+ return entities.stream().anyMatch(e -> e.getType() == type);
+ }
+
+ public void intersectsWithFuture(int bitMask, Consumer intersects) {
+ String bitMaskString = String.valueOf(bitMask) + "%%__NONCE__%%";
+ this.intersects.add(new Triad<>(new Double[] {width, height, shift}, bitMask, intersects));
+ }
+
+ public void collidesWithFuture(int bitMask, Consumer collides) {
+ this.collides.add(new Triad<>(new Double[] {width, height, shift}, bitMask, collides));
+ }
+
+ public boolean isCollidedWithEntity(SimpleCollisionBox box) {
+ for(Entity entity : entities) {
+ if(EntityData.getEntityBox(entity.getLocation(), entity).isCollided(box))
+ return true;
+ }
+ return false;
+ }
+
+ public boolean isCollidedWithEntity() {
+ SimpleCollisionBox playerBox = new SimpleCollisionBox()
+ .offset(location.x, location.y, location.z)
+ .expandMin(0, shift, 0)
+ .expandMax(0, height, 0)
+ .expand(width / 2, 0, width / 2);
+
+ return isCollidedWithEntity(playerBox);
+ }
+
+ public List getCollisionBoxes(SimpleCollisionBox playerBox) {
+ List collided = new ArrayList<>();
+
+ for (Block b : blocks) {
+ Material material = b.getType();
+
+ CollisionBox box;
+ if((box = BlockData.getData(material).getBox(b, ProtocolVersion.getGameVersion())).isCollided(playerBox)) {
+ collided.add(box);
+ }
+ }
+
+ for(Entity entity : entities) {
+ CollisionBox box = EntityData.getEntityBox(entity.getLocation(), entity);
+ if(box.isCollided(playerBox))
+ collided.add(box);
+ }
+
+ return collided;
+ }
+ public List getCollisionBoxes() {
+ SimpleCollisionBox playerBox = new SimpleCollisionBox()
+ .offset(location.x, location.y, location.z)
+ .expandMin(0, shift, 0)
+ .expandMax(0, height, 0)
+ .expand(width / 2, 0, width / 2);
+
+ return getCollisionBoxes(playerBox);
+ }
+
+ public boolean isCollidedWith(CollisionBox box) {
+ SimpleCollisionBox playerBox = new SimpleCollisionBox()
+ .offset(location.x, location.y, location.z)
+ .expandMin(0, shift, 0)
+ .expandMax(0, height, 0)
+ .expand(width / 2, 0, width / 2);
+
+ return box.isCollided(playerBox);
+ }
+
+ public boolean isCollidedWith(SimpleCollisionBox playerBox, Material... materials) {
+ for (Block b : blocks) {
+ Location block = b.getLocation();
+ Material material = b.getType();
+
+ if (materials.length == 0 || MiscUtils.contains(materials, material)) {
+ if (BlockData.getData(material).getBox(b, ProtocolVersion.getGameVersion()).isCollided(playerBox))
+ return true;
+ }
+ }
+
+ if(materials.length == 0) {
+ for(Entity entity : entities) {
+ if(EntityData.getEntityBox(entity.getLocation(), entity).isCollided(playerBox))
+ return true;
+ }
+ }
+
+ return false;
+ }
+ public boolean isCollidedWith(Material... materials) {
+ SimpleCollisionBox playerBox = new SimpleCollisionBox()
+ .offset(location.x, location.y, location.z)
+ .expandMin(0, shift, 0)
+ .expandMax(0, height, 0)
+ .expand(width / 2, 0, width / 2);
+
+ return isCollidedWith(playerBox, materials);
+ }
+
+ public void runFutures() {
+ Triad> value = null;
+ Queue> successful = new LinkedList<>();
+ //To remove objects
+ Queue>> collisionRemove = new LinkedList<>(),
+ intersectsRemove = new LinkedList<>();
+
+ for (Block b : blocks) {
+ Location block = b.getLocation();
+ Material material = b.getType();
+ for (Triad> intersect : intersects) {
+ if(!Materials.checkFlag(material, intersect.second)) continue;
+
+ SimpleCollisionBox playerBox = new SimpleCollisionBox()
+ .offset(location.x, location.y, location.z)
+ .expandMin(0, intersect.first[2], 0)
+ .expandMax(0, intersect.first[1], 0)
+ .expand(intersect.first[0] / 2, 0, intersect.first[0] / 2);
+
+ if (BlockData.getData(material).getBox(b, ProtocolVersion.getGameVersion()).isIntersected(playerBox)) {
+ successful.add(intersect.third);
+ intersectsRemove.add(intersect);
+ }
+ }
+ for (Triad> collides : collides) {
+ if(!Materials.checkFlag(material, collides.second)) continue;
+
+ SimpleCollisionBox playerBox = new SimpleCollisionBox()
+ .offset(location.x, location.y, location.z)
+ .expandMin(0, collides.first[2], 0)
+ .expandMax(0, collides.first[1], 0)
+ .expand(collides.first[0] / 2, 0, collides.first[0] / 2);
+
+ if (BlockData.getData(material).getBox(b, ProtocolVersion.getGameVersion()).isCollided(playerBox)) {
+ successful.add(collides.third);
+ intersectsRemove.add(collides);
+ }
+ }
+
+ while((value = intersectsRemove.poll()) != null) {
+ intersects.remove(value);
+ }
+ while((value = collisionRemove.poll()) != null) {
+ collides.remove(value);
+ }
+ }
+ collides.clear();
+ intersects.clear();
+
+ Consumer consumer = null;
+ while((consumer = successful.poll()) != null) {
+ consumer.accept(true);
+ }
+ }
+}
diff --git a/src/main/java/dev/brighten/ac/utils/HookedListWrapper.java b/src/main/java/dev/brighten/ac/utils/HookedListWrapper.java
new file mode 100644
index 0000000..4a6ac98
--- /dev/null
+++ b/src/main/java/dev/brighten/ac/utils/HookedListWrapper.java
@@ -0,0 +1,132 @@
+package dev.brighten.ac.utils;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+@SuppressWarnings({"unchecked"})
+public abstract class HookedListWrapper extends ListWrapper {
+ public HookedListWrapper(List base) {
+ super(base);
+ }
+
+ // We can use the List#size call to execute some code
+ public abstract void onSize();
+
+ @Override
+ public int size() {
+ this.onSize();
+ return this.base.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return this.base.isEmpty();
+ }
+
+ @Override
+ public boolean contains(Object o) {
+ return this.base.contains(o);
+ }
+
+ @Override
+ public Iterator iterator() {
+ return this.listIterator();
+ }
+
+ @Override
+ public Object[] toArray() {
+ return this.base.toArray();
+ }
+
+ @Override
+ public boolean add(T o) {
+ return this.base.add(o);
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ return this.base.remove(o);
+ }
+
+ @Override
+ public boolean addAll(Collection c) {
+ return this.base.addAll(c);
+ }
+
+ @Override
+ public boolean addAll(int index, Collection c) {
+ return this.base.addAll(index, c);
+ }
+
+ @Override
+ public void clear() {
+ this.base.clear();
+ }
+
+ @Override
+ public T get(int index) {
+ return this.base.get(index);
+ }
+
+ @Override
+ public T set(int index, T element) {
+ return this.base.set(index, element);
+ }
+
+ @Override
+ public void add(int index, T element) {
+ this.base.add(index, element);
+ }
+
+ @Override
+ public T remove(int index) {
+ return this.base.remove(index);
+ }
+
+ @Override
+ public int indexOf(Object o) {
+ return this.base.indexOf(o);
+ }
+
+ @Override
+ public int lastIndexOf(Object o) {
+ return this.base.lastIndexOf(o);
+ }
+
+ @Override
+ public ListIterator listIterator() {
+ return this.base.listIterator();
+ }
+
+ @Override
+ public ListIterator listIterator(int index) {
+ return this.base.listIterator(index);
+ }
+
+ @Override
+ public List subList(int fromIndex, int toIndex) {
+ return this.base.subList(fromIndex, toIndex);
+ }
+
+ @Override
+ public boolean retainAll(Collection c) {
+ return this.base.retainAll(c);
+ }
+
+ @Override
+ public boolean removeAll(Collection c) {
+ return this.base.removeAll(c);
+ }
+
+ @Override
+ public boolean containsAll(Collection c) {
+ return this.base.containsAll(c);
+ }
+
+ @Override
+ public Object[] toArray(Object[] a) {
+ return this.base.toArray(a);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/dev/brighten/ac/utils/KLocation.java b/src/main/java/dev/brighten/ac/utils/KLocation.java
index 182627d..ae19d80 100644
--- a/src/main/java/dev/brighten/ac/utils/KLocation.java
+++ b/src/main/java/dev/brighten/ac/utils/KLocation.java
@@ -4,6 +4,8 @@ import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.util.Vector;
+import java.util.Objects;
+
public class KLocation {
public double x, y, z;
public float yaw, pitch;
@@ -55,4 +57,29 @@ public class KLocation {
public KLocation clone() {
return new KLocation(x, y, z, yaw, pitch, timeStamp);
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ KLocation kLocation = (KLocation) o;
+ return Double.compare(kLocation.x, x) == 0 && Double.compare(kLocation.y, y) == 0 && Double.compare(kLocation.z, z) == 0 && Float.compare(kLocation.yaw, yaw) == 0 && Float.compare(kLocation.pitch, pitch) == 0 && timeStamp == kLocation.timeStamp;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(x, y, z, yaw, pitch, timeStamp);
+ }
+
+ @Override
+ public String toString() {
+ return "KLocation{" +
+ "x=" + x +
+ ", y=" + y +
+ ", z=" + z +
+ ", yaw=" + yaw +
+ ", pitch=" + pitch +
+ ", timeStamp=" + timeStamp +
+ '}';
+ }
}
diff --git a/src/main/java/dev/brighten/ac/utils/ListWrapper.java b/src/main/java/dev/brighten/ac/utils/ListWrapper.java
new file mode 100644
index 0000000..df1c5ad
--- /dev/null
+++ b/src/main/java/dev/brighten/ac/utils/ListWrapper.java
@@ -0,0 +1,15 @@
+package dev.brighten.ac.utils;
+
+import java.util.List;
+
+public abstract class ListWrapper implements List {
+ protected final List base;
+
+ public ListWrapper(List base) {
+ this.base = base;
+ }
+
+ public List getBase() {
+ return this.base;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/dev/brighten/ac/utils/MiscUtils.java b/src/main/java/dev/brighten/ac/utils/MiscUtils.java
index 0658a4b..21834c9 100644
--- a/src/main/java/dev/brighten/ac/utils/MiscUtils.java
+++ b/src/main/java/dev/brighten/ac/utils/MiscUtils.java
@@ -6,10 +6,14 @@ import dev.brighten.ac.utils.reflections.impl.CraftReflection;
import dev.brighten.ac.utils.reflections.impl.MinecraftReflection;
import dev.brighten.ac.utils.reflections.types.WrappedClass;
import dev.brighten.ac.utils.reflections.types.WrappedField;
+import dev.brighten.ac.utils.world.types.SimpleCollisionBox;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.TextComponent;
import org.bukkit.Bukkit;
+import org.bukkit.Location;
import org.bukkit.Material;
+import org.bukkit.World;
+import org.bukkit.block.Block;
import org.bukkit.command.CommandSender;
import org.bukkit.command.PluginCommand;
import org.bukkit.command.SimpleCommandMap;
@@ -42,6 +46,54 @@ public class MiscUtils {
return toCheck.toLowerCase().contains(contains.toLowerCase());
}
+ public static boolean isInMaterialBB(World world, SimpleCollisionBox entityBox, XMaterial xmaterial) {
+ int startX = MathHelper.floor_double(entityBox.xMin);
+ int startY = MathHelper.floor_double(entityBox.yMin);
+ int startZ = MathHelper.floor_double(entityBox.zMin);
+ int endX = MathHelper.floor_double(entityBox.xMax + 1D);
+ int endY = MathHelper.floor_double(entityBox.yMax + 1D);
+ int endZ = MathHelper.floor_double(entityBox.zMax + 1D);
+
+ for(int x = startX ; x < endX ; x++) {
+ for(int y = startY ; y < endY ; y++) {
+ for(int z = startZ ; z < endZ ; z++) {
+ Location loc = new Location(world, x, y, z);
+ Optional op = BlockUtils.getBlockAsync(loc);
+
+ if(op.isPresent()) {
+ if(XMaterial.matchXMaterial(op.get().getType()).equals(xmaterial))
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ public static boolean isInMaterialBB(World world, SimpleCollisionBox entityBox, int bitmask) {
+ int startX = MathHelper.floor_double(entityBox.xMin);
+ int startY = MathHelper.floor_double(entityBox.yMin);
+ int startZ = MathHelper.floor_double(entityBox.zMin);
+ int endX = MathHelper.floor_double(entityBox.xMax + 1D);
+ int endY = MathHelper.floor_double(entityBox.yMax + 1D);
+ int endZ = MathHelper.floor_double(entityBox.zMax + 1D);
+
+ for(int x = startX ; x < endX ; x++) {
+ for(int y = startY ; y < endY ; y++) {
+ for(int z = startZ ; z < endZ ; z++) {
+ Location loc = new Location(world, x, y, z);
+ Optional op = BlockUtils.getBlockAsync(loc);
+
+ if(op.isPresent()) {
+ if(Materials.checkFlag(op.get().getType(), bitmask))
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
public static List combineLists(List one, List two) {
List newList = new ArrayList<>();
diff --git a/src/main/java/dev/brighten/ac/utils/MouseFilter.java b/src/main/java/dev/brighten/ac/utils/MouseFilter.java
new file mode 100644
index 0000000..e8307b6
--- /dev/null
+++ b/src/main/java/dev/brighten/ac/utils/MouseFilter.java
@@ -0,0 +1,29 @@
+package dev.brighten.ac.utils;
+
+public class MouseFilter {
+ public float x;
+ public float y;
+ public float z;
+
+ /**
+ * Smooths mouse input
+ */
+ public float smooth(float p_76333_1_, float p_76333_2_) {
+ this.x += p_76333_1_;
+ p_76333_1_ = (this.x - this.y) * p_76333_2_;
+ this.z += (p_76333_1_ - this.z) * 0.5F;
+
+ if (p_76333_1_ > 0.0F && p_76333_1_ > this.z || p_76333_1_ < 0.0F && p_76333_1_ < this.z) {
+ p_76333_1_ = this.z;
+ }
+
+ this.y += p_76333_1_;
+ return p_76333_1_;
+ }
+
+ public void reset() {
+ this.x = 0.0F;
+ this.y = 0.0F;
+ this.z = 0.0F;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/dev/brighten/ac/utils/PastLocation.java b/src/main/java/dev/brighten/ac/utils/PastLocation.java
new file mode 100644
index 0000000..02041eb
--- /dev/null
+++ b/src/main/java/dev/brighten/ac/utils/PastLocation.java
@@ -0,0 +1,95 @@
+package dev.brighten.ac.utils;
+
+import dev.brighten.ac.Anticheat;
+import org.bukkit.Location;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+public class PastLocation {
+ public final LinkedList previousLocations = new LinkedList<>();
+
+ public KLocation getPreviousLocation(int time) {
+ synchronized (previousLocations) {
+ return (this.previousLocations.stream()
+ .min(Comparator.comparing(loc -> Math.abs(time - loc.timeStamp)))
+ .orElse(this.previousLocations.getFirst()));
+ }
+ }
+
+ public List getEstimatedLocation(int currentTime, int ping, int delta) {
+ synchronized (previousLocations) {
+ int tick = currentTime - ping;
+
+ List locs = new ArrayList<>();
+
+ for (KLocation previousLocation : previousLocations) {
+ if (Math.abs(tick - previousLocation.timeStamp) <= delta) {
+ locs.add(previousLocation.clone());
+ }
+ }
+ return locs;
+ }
+ }
+
+ public List getEstimatedLocationByIndex(int ping, int minAdd, int maxAdd) {
+ synchronized (previousLocations) {
+
+ List locs = new ArrayList<>();
+
+ int size = previousLocations.size() - 1;
+ int max = Math.min(size, size - ping + maxAdd),
+ min = Math.max(0, size - Math.max(1, ping) - minAdd);
+
+ for (int i = max; i > min; i--) {
+ locs.add(previousLocations.get(i));
+ }
+ return locs;
+ }
+ }
+
+ public List getEstimatedLocation(long time, long ping) {
+ synchronized (previousLocations) {
+ return this.previousLocations.stream()
+ .filter(loc -> time - loc.timeStamp > 0
+ && time - loc.timeStamp <= ping + (ping < 50 ? 100 : 50))
+ .collect(Collectors.toList());
+ }
+ }
+
+ public List getPreviousRange(long delta) {
+ synchronized (previousLocations) {
+ long stamp = System.currentTimeMillis();
+
+ return this.previousLocations.stream()
+ .filter(loc -> stamp - loc.timeStamp < delta)
+ .collect(Collectors.toList());
+ }
+ }
+
+ public void addLocation(Location location) {
+ synchronized (previousLocations) {
+ if (previousLocations.size() >= 20)
+ previousLocations.removeFirst();
+
+ KLocation loc = new KLocation(location);
+ loc.timeStamp = Anticheat.INSTANCE.getKeepaliveProcessor().tick;
+ previousLocations.add(loc);
+ }
+ }
+
+ public Deque getPreviousLocations() {
+ return previousLocations;
+ }
+
+ public void addLocation(KLocation location) {
+ KLocation loc = location.clone();
+ loc.timeStamp = Anticheat.INSTANCE.getKeepaliveProcessor().tick;
+ synchronized (previousLocations) {
+ if (previousLocations.size() >= 20)
+ previousLocations.removeFirst();
+
+ previousLocations.add(loc);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/dev/brighten/ac/utils/ReflectionUtil.java b/src/main/java/dev/brighten/ac/utils/ReflectionUtil.java
new file mode 100644
index 0000000..2f87f72
--- /dev/null
+++ b/src/main/java/dev/brighten/ac/utils/ReflectionUtil.java
@@ -0,0 +1,100 @@
+package dev.brighten.ac.utils;
+
+import dev.brighten.ac.Anticheat;
+import sun.misc.Unsafe;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+@SuppressWarnings({"ConstantConditions", "unchecked"})
+public final class ReflectionUtil {
+ private static final Unsafe UNSAFE = ReflectionUtil.getUnsafeInstance();
+
+ private static Unsafe getUnsafeInstance() {
+ try {
+ Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
+ unsafeField.setAccessible(true);
+ return (Unsafe) unsafeField.get(null);
+ } catch (NoSuchFieldException | IllegalAccessException e) {
+ Anticheat.INSTANCE.getLogger().severe("Could not locate Unsafe object!");
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ public static Field get(Class> oClass, Class> type, int index) throws NoSuchFieldException {
+ int i = 0;
+ for (Field field : oClass.getDeclaredFields()) {
+ if (field.getType() == type) {
+ if (i == index) {
+ field.setAccessible(true);
+ return field;
+ }
+ i++;
+ }
+ }
+
+ throw new NoSuchFieldException("Could not find field of class " + type.getName() + " with index " + index);
+ }
+
+ public static Field getFieldByClassNames(Class> clazz, String... simpleNames) throws NoSuchFieldException {
+ for (Field field : clazz.getDeclaredFields()) {
+ String typeSimpleName = field.getType().getSimpleName();
+ for (String name : simpleNames) {
+ if (name.equals(typeSimpleName)) {
+ field.setAccessible(true);
+ return field;
+ }
+ }
+ }
+
+ throw new NoSuchFieldException("Could not find field in class " + clazz.getName() + " with names " + Arrays.toString(simpleNames));
+ }
+
+ public static Field getFieldByType(Class> clazz, Class> type) throws NoSuchFieldException {
+ for (Field field : clazz.getDeclaredFields()) {
+ Class> foundType = field.getType();
+ if (type.isAssignableFrom(foundType)) {
+ field.setAccessible(true);
+ return field;
+ }
+ }
+
+ throw new NoSuchFieldException("Could not find field in class " + clazz.getName() + " with type " + type.getName());
+ }
+
+ public static Method getMethodByName(Class> clazz, String name) throws NoSuchMethodException {
+ for (Method method : clazz.getDeclaredMethods()) {
+ if (name.equals(method.getName())) {
+ method.setAccessible(true);
+ return method;
+ }
+ }
+
+ throw new NoSuchMethodException("Could not find method in class " + clazz.getName() + " with name " + name);
+ }
+
+ public static Class> getSuperClassByName(Class> clazz, String simpleName) {
+ if (!clazz.getSimpleName().equals(simpleName)) {
+ Class> superClazz;
+ while((superClazz = clazz.getSuperclass()) != null) {
+ if (superClazz.getSimpleName().equals(simpleName)) {
+ break;
+ }
+ }
+
+ return superClazz;
+ } else {
+ return clazz;
+ }
+ }
+
+ public static void setUnsafe(Object object, Field field, T value) {
+ ReflectionUtil.UNSAFE.putObject(object, ReflectionUtil.UNSAFE.objectFieldOffset(field), value);
+ }
+
+ public static T instantiateUnsafe(Class clazz) throws Exception{
+ return (T) ReflectionUtil.UNSAFE.allocateInstance(clazz);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/dev/brighten/ac/utils/ServerInjector.java b/src/main/java/dev/brighten/ac/utils/ServerInjector.java
new file mode 100644
index 0000000..859c1a2
--- /dev/null
+++ b/src/main/java/dev/brighten/ac/utils/ServerInjector.java
@@ -0,0 +1,67 @@
+package dev.brighten.ac.utils;
+
+import dev.brighten.ac.Anticheat;
+import dev.brighten.ac.utils.reflections.impl.CraftReflection;
+import dev.brighten.ac.utils.reflections.impl.MinecraftReflection;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.ParameterizedType;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+public class ServerInjector {
+ private static final Collection TICKABLE_CLASS_NAMES = Arrays.asList("IUpdatePlayerListBox", "ITickable", "Runnable");
+ private Field hookedField;
+
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ public void inject() throws Exception {
+ // Start end of tick injection
+ Object server = CraftReflection.getMinecraftServer();
+ Class> serverClass = MinecraftReflection.minecraftServer.getParent();
+
+ // Inject our hooked list for end of tick
+ for (Field field : serverClass.getDeclaredFields()) {
+ try {
+ if (field.getType().equals(List.class)) {
+ // Check if type parameters match one of the tickable class names used throughout different versions
+ Class> genericType = (Class>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
+ if (!ServerInjector.TICKABLE_CLASS_NAMES.contains(genericType.getSimpleName())) {
+ continue;
+ }
+
+ field.setAccessible(true);
+
+ // Use a list wrapper to check when the size method is called
+ HookedListWrapper> wrapper = new HookedListWrapper