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 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 annotationClass, + String packageName) { + return scanFile(annotationClass).stream().filter(pkg -> pkg.startsWith(packageName)) + .map(Reflections::getClass).collect(Collectors.toSet()); + } + + public static Set getClasses(Class annotationClass) { + return scanFile(annotationClass).stream().map(Reflections::getClass).collect(Collectors.toSet()); + } + + public static Set scanFile(Class annotationClass) { + return scanFile(annotationClass, new URL[]{Anticheat.class.getProtectionDomain().getCodeSource().getLocation()}); + } + + public static Set scanFile(Class 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 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 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 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 annotationClass, Set plugins) { + if (!ARCHIVE.matches(path.getFileName())) { + return; + } + + try (ZipFile zip = new ZipFile(path.toFile())) { + Enumeration 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((List) field.get(server)) { + @Override + public void onSize() { + Runnable toRun = null; + + while((toRun = Anticheat.INSTANCE.getOnTickEnd().poll()) != null) { + toRun.run(); + } + } + }; + + ReflectionUtil.setUnsafe(server, field, wrapper); + this.hookedField = field; + break; + } + } catch (Exception ignored) { + } + } + } + + public void eject() throws Exception { + // Replace hooked wrapper with original + if (this.hookedField != null) { + Object server = CraftReflection.getMinecraftServer(); + + HookedListWrapper hookedListWrapper = (HookedListWrapper) this.hookedField.get(server); + + ReflectionUtil.setUnsafe(server, this.hookedField, hookedListWrapper.getBase()); + this.hookedField = null; + } + } +} \ No newline at end of file diff --git a/src/main/java/dev/brighten/ac/utils/TickTimer.java b/src/main/java/dev/brighten/ac/utils/TickTimer.java deleted file mode 100644 index 2d25a94..0000000 --- a/src/main/java/dev/brighten/ac/utils/TickTimer.java +++ /dev/null @@ -1,36 +0,0 @@ -package dev.brighten.ac.utils; - - -import dev.brighten.ac.Anticheat; - -public class TickTimer { - private int ticks = Anticheat.INSTANCE.getCurrentTicks(), defaultPassed; - - public TickTimer(int defaultPassed) { - this.defaultPassed = defaultPassed; - } - - public void reset() { - ticks = Anticheat.INSTANCE.getCurrentTicks(); - } - - public boolean hasPassed() { - return Anticheat.INSTANCE.getCurrentTicks() - ticks > defaultPassed; - } - - public boolean hasPassed(int amount) { - return Anticheat.INSTANCE.getCurrentTicks() - ticks > amount; - } - - public boolean hasNotPassed() { - return Anticheat.INSTANCE.getCurrentTicks() - ticks <= defaultPassed; - } - - public boolean hasNotPassed(int amount) { - return Anticheat.INSTANCE.getCurrentTicks() - ticks <= amount; - } - - public int getPassed() { - return Anticheat.INSTANCE.getCurrentTicks() - ticks; - } -} diff --git a/src/main/java/dev/brighten/ac/utils/Triad.java b/src/main/java/dev/brighten/ac/utils/Triad.java new file mode 100644 index 0000000..d0193fd --- /dev/null +++ b/src/main/java/dev/brighten/ac/utils/Triad.java @@ -0,0 +1,27 @@ +package dev.brighten.ac.utils; + +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; + +import java.util.Objects; + +@AllArgsConstructor +@NoArgsConstructor +public class Triad { + public A first; + public B second; + public C third; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Triad triad = (Triad) o; + return Objects.equals(first, triad.first) && Objects.equals(second, triad.second) && Objects.equals(third, triad.third); + } + + @Override + public int hashCode() { + return Objects.hash(first, second, third); + } +} diff --git a/src/main/java/dev/brighten/ac/utils/Tuple.java b/src/main/java/dev/brighten/ac/utils/Tuple.java index 6656128..f2fe76a 100644 --- a/src/main/java/dev/brighten/ac/utils/Tuple.java +++ b/src/main/java/dev/brighten/ac/utils/Tuple.java @@ -2,6 +2,8 @@ package dev.brighten.ac.utils; import lombok.NoArgsConstructor; +import java.util.Objects; + @NoArgsConstructor public class Tuple { public A one; @@ -11,4 +13,25 @@ public class Tuple { this.one = one; this.two = two; } + + public boolean equals(Object object) { + if (object instanceof Tuple) { + Tuple toCompare = (Tuple) object; + + return one.equals(toCompare.one) && two.equals(toCompare.two); + } else return false; + } + + @Override + public String toString() { + return "Tuple{" + + "one=" + one + + ", two=" + two + + '}'; + } + + @Override + public int hashCode() { + return Objects.hash(one, two); + } } diff --git a/src/main/java/dev/brighten/ac/utils/reflections/Reflections.java b/src/main/java/dev/brighten/ac/utils/reflections/Reflections.java index 7448e0e..5a4b681 100644 --- a/src/main/java/dev/brighten/ac/utils/reflections/Reflections.java +++ b/src/main/java/dev/brighten/ac/utils/reflections/Reflections.java @@ -9,7 +9,6 @@ package dev.brighten.ac.utils.reflections; import dev.brighten.ac.packet.ProtocolVersion; -import dev.brighten.ac.utils.ClassScanner; import dev.brighten.ac.utils.objects.QuadFunction; import dev.brighten.ac.utils.objects.TriFunction; import dev.brighten.ac.utils.reflections.types.WrappedClass; @@ -22,18 +21,14 @@ import java.lang.invoke.LambdaMetafactory; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.reflect.Method; -import java.util.Collections; -import java.util.Set; import java.util.function.BiFunction; import java.util.function.Function; -import java.util.regex.Pattern; @Getter public class Reflections { private static final String craftBukkitString; private static final String netMinecraftServerString; private static MethodHandles.Lookup lookup = MethodHandles.lookup(); - private static Set classNames; public static String OBC_PREFIX = Bukkit.getServer().getClass().getPackage().getName(); public static String VERSION = OBC_PREFIX.replace("org.bukkit.craftbukkit", "") .replace(".", ""); @@ -42,13 +37,6 @@ public class Reflections { String version = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3]; craftBukkitString = "org.bukkit.craftbukkit." + version + "."; netMinecraftServerString = "net.minecraft.server." + version + "."; - - try { - classNames = ClassScanner.scanFile2(null, - Class.forName("org.bukkit.craftbukkit.Main")); - } catch(ClassNotFoundException e) { - classNames = Collections.emptySet(); - } } public static boolean classExists(String name) { @@ -69,18 +57,14 @@ public class Reflections { try { return getClass(netMinecraftServerString + name); } catch(Throwable e) { - Pattern toTest = Pattern.compile("\\." + name.replace("$", ".") + "$"); - for (String className : classNames) { - if(!className.startsWith("net.minecraft")) continue; - - if(toTest.matcher(className).find()) { - return getClass(className); - } - } throw new ClassNotFoundException(name); } } + public void hi() { + System.out.println("hi"); + } + public static WrappedClass getClass(String name) { try { return new WrappedClass(Class.forName(name)); diff --git a/src/main/java/dev/brighten/ac/utils/reflections/types/WrappedClass.java b/src/main/java/dev/brighten/ac/utils/reflections/types/WrappedClass.java index 907106a..01c6f96 100644 --- a/src/main/java/dev/brighten/ac/utils/reflections/types/WrappedClass.java +++ b/src/main/java/dev/brighten/ac/utils/reflections/types/WrappedClass.java @@ -100,7 +100,8 @@ public class WrappedClass { } public WrappedConstructor getConstructor() { - val optional = Arrays.stream(this.parent.getConstructors()).filter(cons -> cons.getParameterCount() == 0).findFirst(); + val optional = Arrays.stream(this.parent.getConstructors()) + .filter(cons -> cons.getParameterCount() == 0).findFirst(); return optional.map(constructor -> new WrappedConstructor(this, constructor)).orElse(null); } @@ -160,6 +161,20 @@ public class WrappedClass { return this.getFieldByType(type, 0); } + public WrappedMethod[] getDeclaredMethods() { + return Arrays.stream(getParent().getDeclaredMethods()) + .map(m -> new WrappedMethod(this, m)) + .toArray(WrappedMethod[]::new); + } + + public boolean isAssignableFrom(WrappedClass wrapped) { + return wrapped.getParent().isAssignableFrom(getParent()); + } + + public boolean isAssignableFrom(Class paramClass) { + return getParent().isAssignableFrom(paramClass); + } + public WrappedMethod getMethod(String name, Class... parameters) { String key = name + ";;" + Arrays.stream(parameters).map(Class::getName) .collect(Collectors.joining(",")); diff --git a/src/main/java/dev/brighten/ac/utils/reflections/types/WrappedMethod.java b/src/main/java/dev/brighten/ac/utils/reflections/types/WrappedMethod.java index 1ac3cf4..caedd34 100644 --- a/src/main/java/dev/brighten/ac/utils/reflections/types/WrappedMethod.java +++ b/src/main/java/dev/brighten/ac/utils/reflections/types/WrappedMethod.java @@ -14,7 +14,9 @@ import dev.brighten.ac.utils.objects.TriFunction; import dev.brighten.ac.utils.reflections.Reflections; import lombok.Getter; +import java.lang.annotation.Annotation; import java.lang.reflect.Method; +import java.lang.reflect.Parameter; import java.util.Arrays; import java.util.List; import java.util.function.BiFunction; @@ -69,6 +71,18 @@ public class WrappedMethod { return mfunc.invokeMethod(object, args); } + public boolean isAnnotationPresent(Class annotation) { + return method.isAnnotationPresent(annotation); + } + + public Parameter[] getParameters() { + return method.getParameters(); + } + + public Class[] getParameterTypes() { + return method.getParameterTypes(); + } + public int getModifiers() { return this.method.getModifiers(); } diff --git a/src/main/java/dev/brighten/ac/utils/timer/Timer.java b/src/main/java/dev/brighten/ac/utils/timer/Timer.java new file mode 100644 index 0000000..6c9fa3b --- /dev/null +++ b/src/main/java/dev/brighten/ac/utils/timer/Timer.java @@ -0,0 +1,23 @@ +package dev.brighten.ac.utils.timer; + +public interface Timer { + + boolean isPassed(long stamp); + + boolean isPassed(); + + boolean isNotPassed(long stamp); + + boolean isNotPassed(); + + boolean isReset(); + + int getResetStreak(); + + long getPassed(); + + long getCurrent(); + + void reset(); + +} diff --git a/src/main/java/dev/brighten/ac/utils/timer/impl/MillisTimer.java b/src/main/java/dev/brighten/ac/utils/timer/impl/MillisTimer.java new file mode 100644 index 0000000..485358b --- /dev/null +++ b/src/main/java/dev/brighten/ac/utils/timer/impl/MillisTimer.java @@ -0,0 +1,66 @@ +package dev.brighten.ac.utils.timer.impl; + +import dev.brighten.ac.utils.timer.Timer; + +public class MillisTimer implements Timer { + + private long currentStamp; + private int resetStreak; + private final long defaultPassed; + + public MillisTimer(long defaultPassed) { + this.defaultPassed = defaultPassed; + } + + public MillisTimer() { + defaultPassed = 1000L; + } + + @Override + public boolean isPassed(long stamp) { + return getPassed() > stamp; + } + + @Override + public boolean isPassed() { + return getPassed() > defaultPassed; + } + + @Override + public boolean isNotPassed(long stamp) { + return getPassed() <= stamp; + } + + @Override + public boolean isNotPassed() { + return getPassed() <= defaultPassed; + } + + @Override + public boolean isReset() { + return getPassed() <= 1L; + } + + @Override + public int getResetStreak() { + return resetStreak; + } + + @Override + public long getPassed() { + return System.currentTimeMillis() - currentStamp; + } + + @Override + public long getCurrent() { + return currentStamp; + } + + @Override + public void reset() { + if(getPassed() <= 60L) resetStreak++; + else resetStreak = 0; + + currentStamp = System.currentTimeMillis(); + } +} diff --git a/src/main/java/dev/brighten/ac/utils/timer/impl/TickTimer.java b/src/main/java/dev/brighten/ac/utils/timer/impl/TickTimer.java new file mode 100644 index 0000000..b434fd5 --- /dev/null +++ b/src/main/java/dev/brighten/ac/utils/timer/impl/TickTimer.java @@ -0,0 +1,67 @@ +package dev.brighten.ac.utils.timer.impl; + +import dev.brighten.ac.Anticheat; +import dev.brighten.ac.utils.timer.Timer; + +public class TickTimer implements Timer { + private long currentStamp; + private int resetStreak; + private final long defaultPassed; + + public TickTimer(long defaultPassed) { + this.defaultPassed = defaultPassed; + currentStamp = Anticheat.INSTANCE.getCurrentTick(); + } + + public TickTimer() { + defaultPassed = 20; + } + + @Override + public boolean isPassed(long stamp) { + return getPassed() > stamp; + } + + @Override + public boolean isPassed() { + return getPassed() > defaultPassed; + } + + @Override + public boolean isNotPassed(long stamp) { + return getPassed() <= stamp; + } + + @Override + public boolean isNotPassed() { + return getPassed() <= defaultPassed; + } + + @Override + public boolean isReset() { + return getPassed() == 0; + } + + @Override + public int getResetStreak() { + return resetStreak; + } + + @Override + public long getPassed() { + return Anticheat.INSTANCE.getCurrentTick()- currentStamp; + } + + @Override + public long getCurrent() { + return currentStamp; + } + + @Override + public void reset() { + if(getPassed() <= 1) resetStreak++; + else resetStreak = 0; + + currentStamp = Anticheat.INSTANCE.getCurrentTick(); + } +} diff --git a/src/main/java/dev/brighten/ac/utils/world/types/SimpleCollisionBox.java b/src/main/java/dev/brighten/ac/utils/world/types/SimpleCollisionBox.java index 8bd96ad..b79f775 100644 --- a/src/main/java/dev/brighten/ac/utils/world/types/SimpleCollisionBox.java +++ b/src/main/java/dev/brighten/ac/utils/world/types/SimpleCollisionBox.java @@ -1,6 +1,7 @@ package dev.brighten.ac.utils.world.types; import dev.brighten.ac.utils.BoundingBox; +import dev.brighten.ac.utils.KLocation; import dev.brighten.ac.utils.reflections.impl.MinecraftReflection; import dev.brighten.ac.utils.world.CollisionBox; import org.bukkit.Location; @@ -51,7 +52,11 @@ public class SimpleCollisionBox implements CollisionBox { public SimpleCollisionBox(Vector min, Vector max) { this(min.getX(), min.getY(), min.getZ(), max.getX(), max.getY(), max.getZ()); } - + + public SimpleCollisionBox(KLocation loc, double width, double height) { + this(loc.toVector(), width, height); + } + public SimpleCollisionBox(Location loc, double width, double height) { this(loc.toVector(), width, height); }