├── src └── main │ ├── java │ └── me │ │ └── nik │ │ └── anticheatbase │ │ ├── playerdata │ │ ├── processors │ │ │ ├── Processor.java │ │ │ └── impl │ │ │ │ ├── SensitivityProcessor.java │ │ │ │ ├── SetbackProcessor.java │ │ │ │ └── CinematicProcessor.java │ │ └── data │ │ │ ├── Data.java │ │ │ └── impl │ │ │ ├── CombatData.java │ │ │ ├── VehicleData.java │ │ │ ├── VelocityData.java │ │ │ ├── ConnectionData.java │ │ │ ├── TeleportData.java │ │ │ ├── ActionData.java │ │ │ └── RotationData.java │ │ ├── checks │ │ ├── enums │ │ │ ├── CheckCategory.java │ │ │ └── CheckType.java │ │ ├── annotations │ │ │ ├── Development.java │ │ │ ├── Experimental.java │ │ │ └── Testing.java │ │ ├── types │ │ │ ├── Check.java │ │ │ └── AbstractCheck.java │ │ └── impl │ │ │ └── speed │ │ │ └── SpeedA.java │ │ ├── managers │ │ ├── Initializer.java │ │ ├── logs │ │ │ ├── LogExporter.java │ │ │ ├── PlayerLog.java │ │ │ ├── LogManager.java │ │ │ └── impl │ │ │ │ └── FileExporter.java │ │ ├── themes │ │ │ ├── BaseTheme.java │ │ │ ├── impl │ │ │ │ └── DefaultTheme.java │ │ │ ├── Theme.java │ │ │ └── ThemeManager.java │ │ ├── threads │ │ │ ├── ProfileThread.java │ │ │ └── ThreadManager.java │ │ ├── profile │ │ │ ├── ProfileManager.java │ │ │ └── Profile.java │ │ └── AlertManager.java │ │ ├── utils │ │ ├── custom │ │ │ ├── desync │ │ │ │ ├── DesyncType.java │ │ │ │ └── Desync.java │ │ │ ├── exception │ │ │ │ └── AnticheatException.java │ │ │ ├── PlacedBlock.java │ │ │ ├── Pair.java │ │ │ ├── Teleport.java │ │ │ ├── NearbyEntity.java │ │ │ ├── ConcurrentSampleList.java │ │ │ ├── SampleList.java │ │ │ ├── EffectType.java │ │ │ ├── ExpiringSet.java │ │ │ ├── Exempt.java │ │ │ ├── HitboxExpansion.java │ │ │ ├── PastLocations.java │ │ │ ├── Equipment.java │ │ │ ├── ExpiringMap.java │ │ │ ├── CheckHolder.java │ │ │ └── aim │ │ │ │ └── RotationHeuristics.java │ │ ├── versionutils │ │ │ ├── VersionInstance.java │ │ │ ├── impl │ │ │ │ ├── ViaVersion.java │ │ │ │ ├── ProtocolSupport.java │ │ │ │ └── ProtocolLib.java │ │ │ ├── VersionUtils.java │ │ │ └── ClientVersion.java │ │ ├── tests │ │ │ ├── NanosTest.java │ │ │ └── MillisTest.java │ │ ├── ChatUtils.java │ │ ├── TaskUtils.java │ │ ├── ServerVersion.java │ │ ├── MoveUtils.java │ │ ├── PlayerUtils.java │ │ ├── MathUtils.java │ │ ├── minecraft │ │ │ └── Vec3i.java │ │ ├── MiscUtils.java │ │ └── JsonBuilder.java │ │ ├── processors │ │ └── listeners │ │ │ └── BukkitListener.java │ │ ├── enums │ │ ├── Permissions.java │ │ └── MsgType.java │ │ ├── wrappers │ │ ├── WrapperPlayClientChat.java │ │ ├── WrapperPlayClientWindowClick.java │ │ ├── WrapperPlayServerChat.java │ │ ├── WrapperPlayClientLook.java │ │ ├── WrapperPlayClientBlockDig.java │ │ ├── WrapperPlayClientEntityAction.java │ │ ├── WrapperPlayClientCustomPayload.java │ │ ├── WrapperPlayClientPosition.java │ │ ├── WrapperPlayClientUseEntity.java │ │ ├── PacketWrapper.java │ │ ├── WrapperPlayClientPositionLook.java │ │ └── WrapperPlayServerEntityVelocity.java │ │ ├── tasks │ │ ├── ViolationTask.java │ │ ├── LogsTask.java │ │ └── TickTask.java │ │ ├── nms │ │ ├── NmsManager.java │ │ ├── NmsInstance.java │ │ └── InstanceDefault.java │ │ ├── commands │ │ ├── SubCommand.java │ │ ├── subcommands │ │ │ └── AlertsCommand.java │ │ └── CommandManager.java │ │ ├── listeners │ │ ├── ProfileListener.java │ │ ├── ClientBrandListener.java │ │ └── ViolationListener.java │ │ ├── api │ │ └── events │ │ │ └── AnticheatViolationEvent.java │ │ ├── files │ │ └── commentedfiles │ │ │ └── CommentedFileConfiguration.java │ │ └── Anticheat.java │ └── resources │ └── plugin.yml ├── README.md ├── .gitignore ├── dependency-reduced-pom.xml └── pom.xml /src/main/java/me/nik/anticheatbase/playerdata/processors/Processor.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.playerdata.processors; 2 | 3 | public interface Processor { 4 | void process(); 5 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/checks/enums/CheckCategory.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.checks.enums; 2 | 3 | public enum CheckCategory { 4 | COMBAT, 5 | MOVEMENT, 6 | WORLD 7 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/managers/Initializer.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.managers; 2 | 3 | public interface Initializer { 4 | void initialize(); 5 | 6 | void shutdown(); 7 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/utils/custom/desync/DesyncType.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.utils.custom.desync; 2 | 3 | public enum DesyncType { 4 | BLOCKING, 5 | SNEAKING, 6 | SPRINTING 7 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/playerdata/data/Data.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.playerdata.data; 2 | 3 | import me.nik.anticheatbase.processors.Packet; 4 | 5 | public interface Data { 6 | void process(Packet packet); 7 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/utils/versionutils/VersionInstance.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.utils.versionutils; 2 | 3 | import org.bukkit.entity.Player; 4 | 5 | public interface VersionInstance { 6 | ClientVersion getClientVersion(Player player); 7 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/utils/custom/exception/AnticheatException.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.utils.custom.exception; 2 | 3 | public class AnticheatException extends RuntimeException { 4 | public AnticheatException(String message) { 5 | super(message); 6 | } 7 | } -------------------------------------------------------------------------------- /src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: AnticheatBase 2 | version: ${project.version} 3 | main: me.nik.anticheatbase.Anticheat 4 | api-version: 1.13 5 | authors: [Nik] 6 | prefix: Anticheat 7 | description: ${project.description} 8 | depend: [ProtocolLib] 9 | commands: 10 | anticheat: 11 | description: The main anticheat command! 12 | aliases: [ac] -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/checks/annotations/Development.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.checks.annotations; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | 6 | /** 7 | * Marks the check's state as Development. 8 | */ 9 | @Retention(RetentionPolicy.RUNTIME) 10 | public @interface Development { 11 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/checks/annotations/Experimental.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.checks.annotations; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | 6 | /** 7 | * Marks the check's state as Experimental. 8 | */ 9 | @Retention(RetentionPolicy.RUNTIME) 10 | public @interface Experimental { 11 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/utils/tests/NanosTest.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.utils.tests; 2 | 3 | public class NanosTest { 4 | 5 | private final long start; 6 | 7 | public NanosTest() { 8 | this.start = System.nanoTime(); 9 | } 10 | 11 | public long getNanos() { 12 | return System.nanoTime() - this.start; 13 | } 14 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/utils/tests/MillisTest.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.utils.tests; 2 | 3 | public class MillisTest { 4 | 5 | private final long start; 6 | 7 | public MillisTest() { 8 | this.start = System.currentTimeMillis(); 9 | } 10 | 11 | public long getMillis() { 12 | return System.currentTimeMillis() - this.start; 13 | } 14 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/checks/annotations/Testing.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.checks.annotations; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | 6 | /** 7 | * This annotation can be used when making a new check, To only make this check get registered. 8 | */ 9 | @Retention(RetentionPolicy.RUNTIME) 10 | public @interface Testing { 11 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/playerdata/data/impl/CombatData.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.playerdata.data.impl; 2 | 3 | import me.nik.anticheatbase.playerdata.data.Data; 4 | import me.nik.anticheatbase.processors.Packet; 5 | 6 | public class CombatData implements Data { 7 | 8 | @Override 9 | public void process(Packet packet) { 10 | /* 11 | Handle the packet 12 | */ 13 | } 14 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/playerdata/data/impl/VehicleData.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.playerdata.data.impl; 2 | 3 | import me.nik.anticheatbase.playerdata.data.Data; 4 | import me.nik.anticheatbase.processors.Packet; 5 | 6 | public class VehicleData implements Data { 7 | 8 | @Override 9 | public void process(Packet packet) { 10 | /* 11 | Handle the packet 12 | */ 13 | } 14 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/playerdata/data/impl/VelocityData.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.playerdata.data.impl; 2 | 3 | import me.nik.anticheatbase.playerdata.data.Data; 4 | import me.nik.anticheatbase.processors.Packet; 5 | 6 | public class VelocityData implements Data { 7 | 8 | @Override 9 | public void process(Packet packet) { 10 | /* 11 | Handle the packet 12 | */ 13 | } 14 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/playerdata/data/impl/ConnectionData.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.playerdata.data.impl; 2 | 3 | import me.nik.anticheatbase.playerdata.data.Data; 4 | import me.nik.anticheatbase.processors.Packet; 5 | 6 | public class ConnectionData implements Data { 7 | 8 | @Override 9 | public void process(Packet packet) { 10 | /* 11 | Handle the packet 12 | */ 13 | } 14 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/processors/listeners/BukkitListener.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.processors.listeners; 2 | 3 | import org.bukkit.event.Listener; 4 | 5 | /** 6 | * A bukkit listener class that we'll use for our bukkit checks and data 7 | *

8 | * NOTE: You shouldn't be using bukkit events in the first place, I just added this for the sake of having it. 9 | */ 10 | public class BukkitListener implements Listener { 11 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/playerdata/data/impl/TeleportData.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.playerdata.data.impl; 2 | 3 | import me.nik.anticheatbase.playerdata.data.Data; 4 | import me.nik.anticheatbase.processors.Packet; 5 | 6 | public class TeleportData implements Data { 7 | 8 | private int teleportTicks; 9 | 10 | @Override 11 | public void process(Packet packet) { 12 | /* 13 | Handle the packet 14 | */ 15 | } 16 | 17 | public int getTeleportTicks() { 18 | return teleportTicks; 19 | } 20 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/utils/versionutils/impl/ViaVersion.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.utils.versionutils.impl; 2 | 3 | import me.nik.anticheatbase.utils.versionutils.ClientVersion; 4 | import me.nik.anticheatbase.utils.versionutils.VersionInstance; 5 | import org.bukkit.entity.Player; 6 | import us.myles.ViaVersion.api.Via; 7 | 8 | public class ViaVersion implements VersionInstance { 9 | @Override 10 | public ClientVersion getClientVersion(Player player) { 11 | return ClientVersion.getClientVersion(Via.getAPI().getPlayerVersion(player)); 12 | } 13 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/enums/Permissions.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.enums; 2 | 3 | /** 4 | * A permissions enumerations class in order to cache our permissions and easily grab them 5 | */ 6 | public enum Permissions { 7 | ADMIN("anticheat.admin"), 8 | BYPASS("anticheat.bypass"), 9 | COMMAND_ALERTS("anticheat.commands.alerts"); 10 | 11 | private final String permission; 12 | 13 | Permissions(String permission) { 14 | this.permission = permission; 15 | } 16 | 17 | public String getPermission() { 18 | return permission; 19 | } 20 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/utils/custom/PlacedBlock.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.utils.custom; 2 | 3 | import org.bukkit.block.BlockFace; 4 | 5 | public class PlacedBlock { 6 | 7 | private final CustomLocation location; 8 | private final BlockFace face; 9 | 10 | public PlacedBlock(CustomLocation location, BlockFace face) { 11 | this.location = location; 12 | this.face = face; 13 | } 14 | 15 | public CustomLocation getLocation() { 16 | return location; 17 | } 18 | 19 | public BlockFace getFace() { 20 | return face; 21 | } 22 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/utils/versionutils/impl/ProtocolSupport.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.utils.versionutils.impl; 2 | 3 | import me.nik.anticheatbase.utils.versionutils.ClientVersion; 4 | import me.nik.anticheatbase.utils.versionutils.VersionInstance; 5 | import org.bukkit.entity.Player; 6 | import protocolsupport.api.ProtocolSupportAPI; 7 | 8 | public class ProtocolSupport implements VersionInstance { 9 | @Override 10 | public ClientVersion getClientVersion(Player player) { 11 | return ClientVersion.getClientVersion(ProtocolSupportAPI.getProtocolVersion(player).getId()); 12 | } 13 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/utils/versionutils/impl/ProtocolLib.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.utils.versionutils.impl; 2 | 3 | import com.comphenix.protocol.ProtocolLibrary; 4 | import me.nik.anticheatbase.utils.versionutils.ClientVersion; 5 | import me.nik.anticheatbase.utils.versionutils.VersionInstance; 6 | import org.bukkit.entity.Player; 7 | 8 | public class ProtocolLib implements VersionInstance { 9 | @Override 10 | public ClientVersion getClientVersion(Player player) { 11 | return ClientVersion.getClientVersion(ProtocolLibrary.getProtocolManager().getProtocolVersion(player)); 12 | } 13 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/checks/types/Check.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.checks.types; 2 | 3 | import me.nik.anticheatbase.checks.enums.CheckType; 4 | import me.nik.anticheatbase.managers.profile.Profile; 5 | import me.nik.anticheatbase.processors.Packet; 6 | 7 | /* 8 | * Abstract class for Checks 9 | */ 10 | public abstract class Check extends AbstractCheck { 11 | 12 | public Check(Profile profile, CheckType check, String type, String description) { 13 | super(profile, check, type, description); 14 | } 15 | 16 | public Check(Profile profile, CheckType check, String description) { 17 | super(profile, check, "", description); 18 | } 19 | 20 | public abstract void handle(Packet packet); 21 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/wrappers/WrapperPlayClientChat.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.wrappers; 2 | 3 | import com.comphenix.protocol.PacketType; 4 | import com.comphenix.protocol.events.PacketContainer; 5 | 6 | public class WrapperPlayClientChat extends PacketWrapper { 7 | 8 | public static final PacketType TYPE = PacketType.Play.Client.CHAT; 9 | 10 | private final String message; 11 | 12 | public WrapperPlayClientChat(PacketContainer packet) { 13 | super(packet, TYPE); 14 | 15 | this.message = handle.getStrings().read(0); 16 | } 17 | 18 | /** 19 | * Retrieve Message. 20 | * 21 | * @return The current Message 22 | */ 23 | public String getMessage() { 24 | return message; 25 | } 26 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/tasks/ViolationTask.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.tasks; 2 | 3 | import me.nik.anticheatbase.Anticheat; 4 | import me.nik.anticheatbase.checks.types.Check; 5 | import org.bukkit.scheduler.BukkitRunnable; 6 | 7 | /** 8 | * A task that we'll be using in order to clear the profile violations. 9 | */ 10 | public class ViolationTask extends BukkitRunnable { 11 | 12 | private final Anticheat plugin; 13 | 14 | public ViolationTask(Anticheat plugin) { 15 | this.plugin = plugin; 16 | } 17 | 18 | @Override 19 | public void run() { 20 | this.plugin.getProfileManager().getProfileMap().values().forEach(profile -> { 21 | for (Check check : profile.getCheckHolder().getChecks()) check.resetVl(); 22 | }); 23 | } 24 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/utils/ChatUtils.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.utils; 2 | 3 | import me.nik.anticheatbase.Anticheat; 4 | import org.bukkit.ChatColor; 5 | 6 | import java.util.regex.Pattern; 7 | 8 | public final class ChatUtils { 9 | 10 | private ChatUtils() { 11 | } 12 | 13 | private static final Pattern STRIP_COLOR_PATTERN = Pattern.compile("(?i)" + '&' + "[0-9A-FK-OR]"); 14 | 15 | public static String format(final String msg) { 16 | return ChatColor.translateAlternateColorCodes('&', msg); 17 | } 18 | 19 | public static String stripColorCodes(final String input) { 20 | return ChatColor.stripColor(STRIP_COLOR_PATTERN.matcher(input).replaceAll("")); 21 | } 22 | 23 | public static void log(final String message) { 24 | Anticheat.getInstance().getLogger().info(message); 25 | } 26 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/tasks/LogsTask.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.tasks; 2 | 3 | import me.nik.anticheatbase.Anticheat; 4 | import org.bukkit.scheduler.BukkitRunnable; 5 | 6 | /** 7 | * A task that we'll be using in order to process our logs. 8 | */ 9 | public class LogsTask extends BukkitRunnable { 10 | 11 | private final Anticheat plugin; 12 | 13 | public LogsTask(Anticheat plugin) { 14 | this.plugin = plugin; 15 | } 16 | 17 | @Override 18 | public void run() { 19 | 20 | if (this.plugin.getLogManager().isLogging() || this.plugin.getLogManager().getLogsQueue().isEmpty()) return; 21 | 22 | this.plugin.getLogManager().getLogExporter().logMultiple(this.plugin.getLogManager().getLogsQueue()); 23 | 24 | this.plugin.getLogManager().clearQueuedLogs(); 25 | 26 | this.plugin.getLogManager().setLogging(false); 27 | } 28 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/utils/custom/Pair.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.utils.custom; 2 | 3 | public class Pair { 4 | 5 | private T key; 6 | private Y value; 7 | 8 | public Pair(T key, Y value) { 9 | this.key = key; 10 | this.value = value; 11 | } 12 | 13 | public Pair(Pair pair) { 14 | this.key = pair.key; 15 | this.value = pair.value; 16 | } 17 | 18 | public T getKey() { 19 | return key; 20 | } 21 | 22 | public void setKey(T key) { 23 | this.key = key; 24 | } 25 | 26 | public Y getValue() { 27 | return value; 28 | } 29 | 30 | public void setValue(Y value) { 31 | this.value = value; 32 | } 33 | 34 | @Override 35 | public String toString() { 36 | return "Pair{" + 37 | "key=" + key + 38 | ", value=" + value + 39 | '}'; 40 | } 41 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/wrappers/WrapperPlayClientWindowClick.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.wrappers; 2 | 3 | import com.comphenix.protocol.PacketType; 4 | import com.comphenix.protocol.events.PacketContainer; 5 | import me.nik.anticheatbase.utils.ServerVersion; 6 | 7 | public class WrapperPlayClientWindowClick extends PacketWrapper { 8 | 9 | public static final PacketType TYPE = PacketType.Play.Client.WINDOW_CLICK; 10 | 11 | private final int slot; 12 | 13 | public WrapperPlayClientWindowClick(PacketContainer packet) { 14 | super(packet, TYPE); 15 | 16 | this.slot = handle.getIntegers().read(ServerVersion.getVersion().isLowerThan(ServerVersion.v1_17_R1) ? 2 : 1); 17 | } 18 | 19 | /** 20 | * Retrieve Slot. 21 | *

22 | * Notes: the clicked slot. See below. 23 | * 24 | * @return The current Slot 25 | */ 26 | public int getSlot() { 27 | return slot; 28 | } 29 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/managers/logs/LogExporter.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.managers.logs; 2 | 3 | import me.nik.anticheatbase.Anticheat; 4 | import me.nik.anticheatbase.files.Config; 5 | 6 | import java.util.Collection; 7 | import java.util.List; 8 | import java.util.concurrent.TimeUnit; 9 | 10 | public abstract class LogExporter { 11 | 12 | protected static final long DELETE_DAYS = TimeUnit.DAYS.toMillis(Config.Setting.LOGS_CLEAR_DAYS.getInt()); 13 | 14 | protected final Anticheat plugin; 15 | 16 | public LogExporter(Anticheat plugin) { 17 | this.plugin = plugin; 18 | } 19 | 20 | public abstract void initialize(); 21 | 22 | public abstract void shutdown(); 23 | 24 | public abstract void logMultiple(Collection logs); 25 | 26 | public abstract void log(PlayerLog log); 27 | 28 | public abstract List getLogs(); 29 | 30 | public abstract List getLogsForPlayer(String player); 31 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/utils/custom/Teleport.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.utils.custom; 2 | 3 | public class Teleport { 4 | 5 | private final double x, y, z; 6 | private final long timeStamp; 7 | 8 | public Teleport(double x, double y, double z, long timeStamp) { 9 | this.x = x; 10 | this.y = y; 11 | this.z = z; 12 | this.timeStamp = timeStamp; 13 | } 14 | 15 | public boolean matches(double x, double y, double z) { 16 | /* 17 | Check if it's less than .03125D due to precision loss in rare cases 18 | */ 19 | return Math.abs(this.x - x) < .03125D && Math.abs(this.y - y) < .03125D && Math.abs(this.z - z) < .03125D; 20 | } 21 | 22 | public double getX() { 23 | return x; 24 | } 25 | 26 | public double getY() { 27 | return y; 28 | } 29 | 30 | public double getZ() { 31 | return z; 32 | } 33 | 34 | public long getTimeStamp() { 35 | return timeStamp; 36 | } 37 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/nms/NmsManager.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.nms; 2 | 3 | import me.nik.anticheatbase.utils.ServerVersion; 4 | 5 | /** 6 | * A simple NMS Manager class 7 | *

8 | * NOTE: Obviously this is not done, You should implement every single nms version yourself 9 | * Inside the me.nik.anticheatbase.manager.managers.nms.impl package. 10 | *

11 | * NMS Can improve perfomance by a LOT even when calling simple methods such as p.getAllowFlight(); 12 | * YourKit profiler doesn't lie! 13 | */ 14 | public class NmsManager { 15 | 16 | private final NmsInstance nmsInstance; 17 | 18 | public NmsManager() { 19 | 20 | switch (ServerVersion.getVersion()) { 21 | 22 | default: 23 | this.nmsInstance = new InstanceDefault(); 24 | break; 25 | 26 | /* 27 | Add more versions here. 28 | */ 29 | } 30 | } 31 | 32 | public NmsInstance getNmsInstance() { 33 | return nmsInstance; 34 | } 35 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/utils/custom/NearbyEntity.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.utils.custom; 2 | 3 | import org.bukkit.entity.Entity; 4 | 5 | /** 6 | * A nearby entity class storing all the information we need about nearby entities 7 | * So we can later use this in case we need to check if an specific entity is nearby 8 | * Or within a certain distance. 9 | */ 10 | public class NearbyEntity { 11 | 12 | private final Entity entity; 13 | private final double horizontalDistance, verticalDistance; 14 | 15 | public NearbyEntity(Entity entity, double horizontalDistance, double verticalDistance) { 16 | this.entity = entity; 17 | this.horizontalDistance = horizontalDistance; 18 | this.verticalDistance = verticalDistance; 19 | } 20 | 21 | public Entity getEntity() { 22 | return entity; 23 | } 24 | 25 | public double getHorizontalDistance() { 26 | return horizontalDistance; 27 | } 28 | 29 | public double getVerticalDistance() { 30 | return verticalDistance; 31 | } 32 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/utils/custom/ConcurrentSampleList.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.utils.custom; 2 | 3 | import java.util.concurrent.ConcurrentLinkedDeque; 4 | 5 | /** 6 | * A custom concurrent sample list that we'll be using on our checks. 7 | */ 8 | public final class ConcurrentSampleList extends ConcurrentLinkedDeque { 9 | 10 | private final int sampleSize; 11 | private final boolean update; 12 | 13 | public ConcurrentSampleList(int sampleSize) { 14 | this.sampleSize = sampleSize; 15 | this.update = false; 16 | } 17 | 18 | public ConcurrentSampleList(int sampleSize, boolean update) { 19 | this.sampleSize = sampleSize; 20 | this.update = update; 21 | } 22 | 23 | @Override 24 | public boolean add(T t) { 25 | if (isCollected()) { 26 | if (this.update) { 27 | super.removeFirst(); 28 | } else super.clear(); 29 | } 30 | 31 | return super.add(t); 32 | } 33 | 34 | public boolean isCollected() { 35 | return super.size() >= this.sampleSize; 36 | } 37 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/utils/custom/SampleList.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.utils.custom; 2 | 3 | import java.util.LinkedList; 4 | 5 | /** 6 | * A custom sample list that we'll be using on our checks. 7 | */ 8 | public final class SampleList extends LinkedList { 9 | 10 | private final int sampleSize; 11 | private final boolean update; 12 | 13 | public SampleList(int sampleSize) { 14 | this.sampleSize = sampleSize; 15 | this.update = false; 16 | } 17 | 18 | public SampleList(int sampleSize, boolean update) { 19 | this.sampleSize = sampleSize; 20 | this.update = update; 21 | } 22 | 23 | @Override 24 | public boolean add(T t) { 25 | if (isCollected()) { 26 | if (this.update) { 27 | super.removeFirst(); 28 | } else super.clear(); 29 | } 30 | 31 | return super.add(t); 32 | } 33 | 34 | public int getMaxSize() { 35 | return sampleSize; 36 | } 37 | 38 | public boolean isCollected() { 39 | return super.size() >= this.sampleSize; 40 | } 41 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/checks/impl/speed/SpeedA.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.checks.impl.speed; 2 | 3 | import me.nik.anticheatbase.checks.enums.CheckType; 4 | import me.nik.anticheatbase.checks.types.Check; 5 | import me.nik.anticheatbase.managers.profile.Profile; 6 | import me.nik.anticheatbase.playerdata.data.impl.MovementData; 7 | import me.nik.anticheatbase.processors.Packet; 8 | 9 | public class SpeedA extends Check { 10 | public SpeedA(Profile profile) { 11 | super(profile, CheckType.SPEED, "A", "Checks for speed"); 12 | } 13 | 14 | @Override 15 | public void handle(Packet packet) { 16 | if (!packet.isMovement() || profile.isExempt().movement()) return; 17 | 18 | //Let's make an example MEME check 19 | 20 | MovementData data = profile.getMovementData(); 21 | 22 | if (data.getDeltaXZ() > Double.MIN_VALUE) { 23 | 24 | if (increaseBuffer() > 50) fail( 25 | "FLAGGED BY THE BEST SPEED CHECK EVER IN EXISTENCE, Credits to Elon Musk." 26 | ); 27 | 28 | } else decreaseBufferBy(Float.MIN_VALUE); 29 | } 30 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/managers/themes/BaseTheme.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.managers.themes; 2 | 3 | import me.nik.anticheatbase.Anticheat; 4 | import me.nik.anticheatbase.utils.MiscUtils; 5 | import org.bukkit.configuration.file.FileConfiguration; 6 | 7 | import java.io.File; 8 | import java.io.IOException; 9 | 10 | public abstract class BaseTheme { 11 | 12 | private final FileConfiguration data; 13 | 14 | public BaseTheme(Anticheat plugin, String themeName) { 15 | 16 | File file = new File(plugin.getDataFolder() + "/themes", themeName + ".yml"); 17 | 18 | try { 19 | 20 | file.createNewFile(); 21 | 22 | } catch (IOException ignored) { 23 | } 24 | 25 | this.data = MiscUtils.loadConfigurationUTF_8(file); 26 | 27 | create(); 28 | 29 | get().options().copyDefaults(true); 30 | 31 | try { 32 | 33 | this.data.save(file); 34 | 35 | } catch (IOException ignored) { 36 | } 37 | } 38 | 39 | protected FileConfiguration get() { 40 | return this.data; 41 | } 42 | 43 | public abstract void create(); 44 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/utils/versionutils/VersionUtils.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.utils.versionutils; 2 | 3 | import me.nik.anticheatbase.utils.versionutils.impl.ProtocolLib; 4 | import me.nik.anticheatbase.utils.versionutils.impl.ProtocolSupport; 5 | import me.nik.anticheatbase.utils.versionutils.impl.ViaVersion; 6 | import org.bukkit.Bukkit; 7 | import org.bukkit.entity.Player; 8 | import org.bukkit.plugin.PluginManager; 9 | 10 | public final class VersionUtils { 11 | 12 | private static final VersionInstance VERSION_INSTANCE; 13 | 14 | static { 15 | 16 | PluginManager pm = Bukkit.getPluginManager(); 17 | 18 | if (pm.isPluginEnabled("ViaVersion")) { 19 | 20 | VERSION_INSTANCE = new ViaVersion(); 21 | 22 | } else if (pm.isPluginEnabled("ProtocolSupport")) { 23 | 24 | VERSION_INSTANCE = new ProtocolSupport(); 25 | 26 | } else VERSION_INSTANCE = new ProtocolLib(); 27 | } 28 | 29 | private VersionUtils() { 30 | } 31 | 32 | public static ClientVersion getClientVersion(final Player player) { 33 | return VERSION_INSTANCE.getClientVersion(player); 34 | } 35 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/managers/threads/ProfileThread.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.managers.threads; 2 | 3 | import java.util.concurrent.ExecutorService; 4 | import java.util.concurrent.Executors; 5 | 6 | /** 7 | * A simple class to track the amount of profiles using a selected thread. 8 | */ 9 | public class ProfileThread { 10 | 11 | private final ExecutorService thread = Executors.newSingleThreadExecutor(); 12 | 13 | private int profileCount; 14 | 15 | public void execute(Runnable runnable) { 16 | 17 | //Fixes strange issues when the thread has been shut down and the packet has been delayed 18 | if (this.thread.isShutdown()) return; 19 | 20 | this.thread.execute(runnable); 21 | } 22 | 23 | public int getProfileCount() { 24 | return this.profileCount; 25 | } 26 | 27 | public ProfileThread incrementAndGet() { 28 | 29 | this.profileCount++; 30 | 31 | return this; 32 | } 33 | 34 | public void decrement() { 35 | this.profileCount--; 36 | } 37 | 38 | public ProfileThread shutdownThread() { 39 | 40 | this.thread.shutdownNow(); 41 | 42 | return this; 43 | } 44 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/utils/custom/EffectType.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.utils.custom; 2 | 3 | /** 4 | * A effects enumeration class in order to grab the matching Effect Type 5 | * Based on the potion ID. 6 | */ 7 | public enum EffectType { 8 | UNKNOWN, 9 | SPEED, 10 | SLOWNESS, 11 | HASTE, 12 | MINING_FATIGUE, 13 | STRENGTH, 14 | INSTANT_HEALTH, 15 | INSTANT_DAMAGE, 16 | JUMP_BOOST, 17 | NAUSEA, 18 | REGENERATION, 19 | RESISTANCE, 20 | FIRE_RESISTANCE, 21 | WATER_BREATHING, 22 | INVISIBILITY, 23 | BLINDNESS, 24 | NIGHT_VISION, 25 | HUNGER, 26 | WEAKNESS, 27 | POISON, 28 | WITHER, 29 | HEALTH_BOOST, 30 | ABSORPTION, 31 | SATURATION, 32 | GLOWING, 33 | LEVITATION, 34 | LUCK, 35 | BAD_LUCK, 36 | SLOW_FALLING, 37 | CONDUIT_POWER, 38 | DOLPHINS_GRACE, 39 | BAD_OMEN, 40 | HERO_OF_THE_VILLAGE; 41 | 42 | /** 43 | * Get the effect type based on the id 44 | * 45 | * @param id The id 46 | * @return The matching effect type if present, Otherwise this method will return UNKNOWN. 47 | */ 48 | public static EffectType fromID(int id) { 49 | return id >= EffectType.values().length ? UNKNOWN : EffectType.values()[id]; 50 | } 51 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/managers/themes/impl/DefaultTheme.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.managers.themes.impl; 2 | 3 | import me.nik.anticheatbase.Anticheat; 4 | import me.nik.anticheatbase.managers.themes.BaseTheme; 5 | 6 | import java.util.Arrays; 7 | 8 | public class DefaultTheme extends BaseTheme { 9 | public DefaultTheme(Anticheat plugin, String themeName) { 10 | super(plugin, themeName); 11 | } 12 | 13 | @Override 14 | public void create() { 15 | get().addDefault("prefix", "&8「&cAnticheat&8」&7»&r "); 16 | get().addDefault("no_perm", "&cYou do not have permission to do that!"); 17 | get().addDefault("console_commands", "&c&lYou cannot run this command through the console :("); 18 | get().addDefault("alert_message", "&7%player% &ffailed &c%check% &fx%vl%"); 19 | get().addDefault("alert_hover", 20 | Arrays.asList( 21 | "&7Description:&r", 22 | "%description%", 23 | "", 24 | "&7Information:&r", 25 | "%information%", 26 | "", 27 | "&7TPS: &r%tps%", 28 | "", 29 | "&fClick to teleport" 30 | )); 31 | } 32 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/nms/NmsInstance.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.nms; 2 | 3 | import org.bukkit.Material; 4 | import org.bukkit.World; 5 | import org.bukkit.block.Block; 6 | import org.bukkit.entity.Entity; 7 | import org.bukkit.entity.Player; 8 | import org.bukkit.inventory.ItemStack; 9 | 10 | public interface NmsInstance { 11 | 12 | float getAttackCooldown(Player player); 13 | 14 | boolean isChunkLoaded(World world, int x, int z); 15 | 16 | Material getType(Block block); 17 | 18 | Entity[] getChunkEntities(World world, int x, int z); 19 | 20 | boolean isWaterLogged(Block block); 21 | 22 | boolean isDead(Player player); 23 | 24 | boolean isSleeping(Player player); 25 | 26 | boolean isGliding(Player player); 27 | 28 | boolean isInsideVehicle(Player player); 29 | 30 | boolean isRiptiding(Player player); 31 | 32 | boolean isBlocking(Player player); 33 | 34 | boolean isSneaking(Player player); 35 | 36 | ItemStack getItemInMainHand(Player player); 37 | 38 | ItemStack getItemInOffHand(Player player); 39 | 40 | float getWalkSpeed(Player player); 41 | 42 | float getAttributeSpeed(Player player); 43 | 44 | boolean getAllowFlight(Player player); 45 | 46 | boolean isFlying(Player player); 47 | 48 | float getFallDistance(Player player); 49 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/commands/SubCommand.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.commands; 2 | 3 | import org.bukkit.command.CommandSender; 4 | 5 | /** 6 | * A subcommand class that we'll be using to extend on our commands 7 | */ 8 | public abstract class SubCommand { 9 | 10 | /** 11 | * @return The command's name 12 | */ 13 | protected abstract String getName(); 14 | 15 | /** 16 | * @return The command's description 17 | */ 18 | protected abstract String getDescription(); 19 | 20 | /** 21 | * @return The command's syntax 22 | */ 23 | protected abstract String getSyntax(); 24 | 25 | /** 26 | * @return The permission required in order to run this command 27 | */ 28 | protected abstract String getPermission(); 29 | 30 | /** 31 | * @return The maximum arguments for this command 32 | */ 33 | protected abstract int maxArguments(); 34 | 35 | /** 36 | * @return Whether this command can be executed through the console 37 | */ 38 | protected abstract boolean canConsoleExecute(); 39 | 40 | /** 41 | * The method that will be run once the command is executed 42 | * 43 | * @param sender Sender 44 | * @param args Args 45 | */ 46 | protected abstract void perform(CommandSender sender, String[] args); 47 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/managers/themes/Theme.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.managers.themes; 2 | 3 | import me.nik.anticheatbase.utils.MiscUtils; 4 | import org.bukkit.configuration.file.FileConfiguration; 5 | 6 | import java.io.File; 7 | import java.io.IOException; 8 | 9 | public class Theme { 10 | 11 | private final File file; 12 | private final String name; 13 | private FileConfiguration config; 14 | 15 | public Theme(File file) { 16 | this.file = file; 17 | this.name = file.getName().replace(".yml", ""); 18 | reload(); 19 | } 20 | 21 | public void reload() { 22 | try { 23 | this.config = MiscUtils.loadConfigurationUTF_8(this.file); 24 | this.config.save(this.file); 25 | } catch (IOException e) { 26 | e.printStackTrace(); 27 | } 28 | } 29 | 30 | public File getFile() { 31 | return this.file; 32 | } 33 | 34 | public String getAuthor() { 35 | return this.config.getString("theme_author"); 36 | } 37 | 38 | public String getPrefix() { 39 | return this.config.getString("prefix"); 40 | } 41 | 42 | public FileConfiguration getConfig() { 43 | return this.config; 44 | } 45 | 46 | public String getString(String path) { 47 | return this.config.getString(path, "null"); 48 | } 49 | 50 | public String getThemeName() { 51 | return this.name; 52 | } 53 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # A simple and efficient Minecraft Anticheat Base! 2 | 3 | A simple Anticheat Base for the game Minecraft, Made with perfomance in mind 4 | This base utilizes ProtocolLib, However it's very easy to add your own Packet Listener 5 | It's highly recommended that you use NMS if you end up using this on a production server 6 | In order to maximize perfomance gain, Especially in newer versions of minecraft. 7 | 8 | ### Features 9 | 10 | * Multithreaded design scaling by your server's hardware. 11 | * Lots of useful Utility classes and methods, Made with perfomance in mind (Including a BetterStream library and FastMath) 12 | * Chat packet based alerts with hoverable messages 13 | * High perfomance Check Manager system 14 | * Easy to use PlayerLog system (SQL implementation can be added easily) 15 | * Easy to use Theme system (With multiple themes) 16 | * Easy to use Configuration system (Value caching) 17 | * Easy to use Command system 18 | * Easy to use NMS system 19 | * Per player checks, Making check creation much easier 20 | * Many check testing tools, Making check development much easier 21 | * Made to support all versions of Minecraft 22 | * High perfomance packet wrapping system with caching 23 | * High perfomance in general 24 | * Access to the player's client version 25 | * Access to the player's client brand 26 | * Zero usage of unnecessary method calls - api usage for the sake of it looking *pretty* 27 | 28 | ## License 29 | None, Feel free to use anything you may find useful. 30 | -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/wrappers/WrapperPlayServerChat.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.wrappers; 2 | 3 | import com.comphenix.protocol.PacketType; 4 | import com.comphenix.protocol.events.PacketContainer; 5 | import com.comphenix.protocol.wrappers.EnumWrappers; 6 | import com.comphenix.protocol.wrappers.WrappedChatComponent; 7 | 8 | public class WrapperPlayServerChat extends PacketWrapper { 9 | public static final PacketType TYPE = PacketType.Play.Server.CHAT; 10 | 11 | public WrapperPlayServerChat() { 12 | super(new PacketContainer(TYPE), TYPE); 13 | handle.getModifier().writeDefaults(); 14 | } 15 | 16 | public WrapperPlayServerChat(PacketContainer packet) { 17 | super(packet, TYPE); 18 | } 19 | 20 | /** 21 | * Retrieve the chat message. 22 | *

23 | * Limited to 32767 bytes 24 | * 25 | * @return The current message 26 | */ 27 | public WrappedChatComponent getMessage() { 28 | return handle.getChatComponents().read(0); 29 | } 30 | 31 | /** 32 | * Set the message. 33 | * 34 | * @param value - new value. 35 | */ 36 | public void setMessage(WrappedChatComponent value) { 37 | handle.getChatComponents().write(0, value); 38 | } 39 | 40 | public EnumWrappers.ChatType getChatType() { 41 | return handle.getChatTypes().read(0); 42 | } 43 | 44 | public void setChatType(EnumWrappers.ChatType type) { 45 | handle.getChatTypes().write(0, type); 46 | } 47 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/listeners/ProfileListener.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.listeners; 2 | 3 | import me.nik.anticheatbase.Anticheat; 4 | import me.nik.anticheatbase.enums.Permissions; 5 | import me.nik.anticheatbase.files.Config; 6 | import org.bukkit.entity.Player; 7 | import org.bukkit.event.EventHandler; 8 | import org.bukkit.event.EventPriority; 9 | import org.bukkit.event.Listener; 10 | import org.bukkit.event.player.PlayerJoinEvent; 11 | import org.bukkit.event.player.PlayerQuitEvent; 12 | 13 | /** 14 | * A profile listener that we'll use in order to initialize our player profile. 15 | */ 16 | public class ProfileListener implements Listener { 17 | 18 | private final Anticheat plugin; 19 | 20 | public ProfileListener(Anticheat plugin) { 21 | this.plugin = plugin; 22 | } 23 | 24 | @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) 25 | public void onJoin(PlayerJoinEvent e) { 26 | 27 | final Player player = e.getPlayer(); 28 | 29 | this.plugin.getProfileManager().createProfile(player); 30 | 31 | if (Config.Setting.TOGGLE_ALERTS_ON_JOIN.getBoolean() && player.hasPermission(Permissions.COMMAND_ALERTS.getPermission())) { 32 | 33 | this.plugin.getAlertManager().addPlayerToAlerts(player.getUniqueId()); 34 | } 35 | } 36 | 37 | @EventHandler(priority = EventPriority.HIGHEST) 38 | public void onLeave(PlayerQuitEvent e) { 39 | this.plugin.getProfileManager().removeProfile(e.getPlayer()); 40 | } 41 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/utils/custom/ExpiringSet.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.utils.custom; 2 | 3 | import com.google.common.cache.Cache; 4 | import com.google.common.cache.CacheBuilder; 5 | import com.google.common.cache.CacheLoader; 6 | 7 | import java.io.Serializable; 8 | import java.util.AbstractSet; 9 | import java.util.Iterator; 10 | import java.util.Set; 11 | import java.util.concurrent.ConcurrentMap; 12 | import java.util.concurrent.TimeUnit; 13 | 14 | public class ExpiringSet extends AbstractSet implements Set, Serializable { 15 | private static final Object PRESENT = new Object(); 16 | 17 | private final ConcurrentMap map; 18 | private final Cache cache; 19 | 20 | public ExpiringSet(long expireMillis) { 21 | this.cache = CacheBuilder.newBuilder() 22 | .expireAfterWrite(expireMillis, TimeUnit.MILLISECONDS) 23 | .build(new CacheLoader() { 24 | public Object load(Object o) { 25 | return PRESENT; 26 | } 27 | }); 28 | 29 | this.map = this.cache.asMap(); 30 | } 31 | 32 | @Override 33 | public Iterator iterator() { 34 | return this.map.keySet().iterator(); 35 | } 36 | 37 | @Override 38 | public int size() { 39 | this.cache.cleanUp(); 40 | return this.map.size(); 41 | } 42 | 43 | @Override 44 | public boolean add(E e) { 45 | return this.map.put(e, ExpiringSet.PRESENT) == null; 46 | } 47 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/managers/profile/ProfileManager.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.managers.profile; 2 | 3 | import me.nik.anticheatbase.managers.Initializer; 4 | import org.bukkit.Bukkit; 5 | import org.bukkit.entity.Player; 6 | 7 | import java.util.Map; 8 | import java.util.Objects; 9 | import java.util.UUID; 10 | import java.util.concurrent.ConcurrentHashMap; 11 | 12 | /** 13 | * A profile manager class that we'll use in order to create or get a player profile. 14 | */ 15 | public class ProfileManager implements Initializer { 16 | 17 | private final Map profiles = new ConcurrentHashMap<>(); 18 | 19 | @Override 20 | public void initialize() { 21 | Bukkit.getOnlinePlayers() 22 | .stream() 23 | .filter(Objects::nonNull) 24 | .forEach(this::createProfile); 25 | } 26 | 27 | public void createProfile(Player player) { 28 | 29 | UUID uuid = player.getUniqueId(); 30 | 31 | if (this.profiles.containsKey(uuid)) return; 32 | 33 | this.profiles.put(uuid, new Profile(player)); 34 | } 35 | 36 | public void removeProfile(Player player) { 37 | this.profiles.remove(player.getUniqueId()); 38 | } 39 | 40 | public Profile getProfile(Player player) { 41 | return this.profiles.get(player.getUniqueId()); 42 | } 43 | 44 | public Map getProfileMap() { 45 | return this.profiles; 46 | } 47 | 48 | @Override 49 | public void shutdown() { 50 | this.profiles.clear(); 51 | } 52 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/utils/TaskUtils.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.utils; 2 | 3 | import me.nik.anticheatbase.Anticheat; 4 | import org.bukkit.Bukkit; 5 | import org.bukkit.scheduler.BukkitTask; 6 | 7 | /** 8 | * A small utility class that we can use in order to create and run tasks quickly 9 | */ 10 | public final class TaskUtils { 11 | 12 | private TaskUtils() { 13 | } 14 | 15 | public static BukkitTask taskTimer(Runnable runnable, long delay, long interval) { 16 | return Bukkit.getScheduler().runTaskTimer(Anticheat.getInstance(), runnable, delay, interval); 17 | } 18 | 19 | public static BukkitTask taskTimerAsync(Runnable runnable, long delay, long interval) { 20 | return Bukkit.getScheduler().runTaskTimerAsynchronously(Anticheat.getInstance(), runnable, delay, interval); 21 | } 22 | 23 | public static BukkitTask task(Runnable runnable) { 24 | return Bukkit.getScheduler().runTask(Anticheat.getInstance(), runnable); 25 | } 26 | 27 | public static BukkitTask taskAsync(Runnable runnable) { 28 | return Bukkit.getScheduler().runTaskAsynchronously(Anticheat.getInstance(), runnable); 29 | } 30 | 31 | public static BukkitTask taskLater(Runnable runnable, long delay) { 32 | return Bukkit.getScheduler().runTaskLater(Anticheat.getInstance(), runnable, delay); 33 | } 34 | 35 | public static BukkitTask taskLaterAsync(Runnable runnable, long delay) { 36 | return Bukkit.getScheduler().runTaskLaterAsynchronously(Anticheat.getInstance(), runnable, delay); 37 | } 38 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/wrappers/WrapperPlayClientLook.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.wrappers; 2 | 3 | import com.comphenix.protocol.PacketType; 4 | import com.comphenix.protocol.events.PacketContainer; 5 | import com.comphenix.protocol.reflect.StructureModifier; 6 | 7 | public class WrapperPlayClientLook extends PacketWrapper { 8 | 9 | public static final PacketType TYPE = PacketType.Play.Client.LOOK; 10 | 11 | private final float yaw, pitch; 12 | private final boolean onGround; 13 | 14 | public WrapperPlayClientLook(PacketContainer packet) { 15 | super(packet, TYPE); 16 | 17 | StructureModifier floats = handle.getFloat(); 18 | 19 | this.yaw = floats.read(0); 20 | this.pitch = floats.read(1); 21 | this.onGround = handle.getBooleans().read(0); 22 | } 23 | 24 | /** 25 | * Retrieve Yaw. 26 | *

27 | * Notes: absolute rotation on the X Axis, in degrees 28 | * 29 | * @return The current Yaw 30 | */ 31 | public float getYaw() { 32 | return yaw; 33 | } 34 | 35 | /** 36 | * Retrieve Pitch. 37 | *

38 | * Notes: absolute rotation on the Y Axis, in degrees 39 | * 40 | * @return The current Pitch 41 | */ 42 | public float getPitch() { 43 | return pitch; 44 | } 45 | 46 | /** 47 | * Retrieve On Ground. 48 | *

49 | * Notes: true if the client is on the ground, False otherwise 50 | * 51 | * @return The current On Ground 52 | */ 53 | public boolean getOnGround() { 54 | return onGround; 55 | } 56 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/wrappers/WrapperPlayClientBlockDig.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.wrappers; 2 | 3 | import com.comphenix.protocol.PacketType; 4 | import com.comphenix.protocol.events.PacketContainer; 5 | import com.comphenix.protocol.wrappers.BlockPosition; 6 | import com.comphenix.protocol.wrappers.EnumWrappers; 7 | 8 | public class WrapperPlayClientBlockDig extends PacketWrapper { 9 | 10 | public static final PacketType TYPE = PacketType.Play.Client.BLOCK_DIG; 11 | 12 | private final BlockPosition location; 13 | private final EnumWrappers.Direction direction; 14 | private final EnumWrappers.PlayerDigType status; 15 | 16 | public WrapperPlayClientBlockDig(PacketContainer packet) { 17 | super(packet, TYPE); 18 | 19 | this.location = handle.getBlockPositionModifier().read(0); 20 | 21 | this.direction = handle.getDirections().read(0); 22 | 23 | this.status = handle.getPlayerDigTypes().read(0); 24 | } 25 | 26 | /** 27 | * Retrieve Location. 28 | *

29 | * Notes: block position 30 | * 31 | * @return The current Location 32 | */ 33 | public BlockPosition getLocation() { 34 | return location; 35 | } 36 | 37 | public EnumWrappers.Direction getDirection() { 38 | return direction; 39 | } 40 | 41 | /** 42 | * Retrieve Status. 43 | *

44 | * Notes: the action the player is taking against the block (see below) 45 | * 46 | * @return The current Status 47 | */ 48 | public EnumWrappers.PlayerDigType getStatus() { 49 | return status; 50 | } 51 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/enums/MsgType.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.enums; 2 | 3 | import me.nik.anticheatbase.Anticheat; 4 | import me.nik.anticheatbase.utils.ChatUtils; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * A message type enumerations class in order to cache our messages from our theme and easily grab them. 10 | */ 11 | public enum MsgType { 12 | PREFIX(ChatUtils.format(Anticheat.getInstance().getThemeManager().getTheme().getString("prefix"))), 13 | NO_PERMISSION(PREFIX.getMessage() + ChatUtils.format(Anticheat.getInstance().getThemeManager().getTheme().getString("no_perm"))), 14 | CONSOLE_COMMANDS(PREFIX.getMessage() + ChatUtils.format(Anticheat.getInstance().getThemeManager().getTheme().getString("console_commands"))), 15 | ALERT_MESSAGE(PREFIX.getMessage() + ChatUtils.format(Anticheat.getInstance().getThemeManager().getTheme().getString("alert_message"))), 16 | ALERT_HOVER(stringFromList(Anticheat.getInstance().getThemeManager().getTheme().getConfig().getStringList("alert_hover"))); 17 | 18 | private final String message; 19 | 20 | MsgType(String message) { 21 | this.message = message; 22 | } 23 | 24 | public String getMessage() { 25 | return message; 26 | } 27 | 28 | private static String stringFromList(List list) { 29 | 30 | StringBuilder sb = new StringBuilder(); 31 | 32 | int size = list.size(); 33 | 34 | for (int i = 0; i < size; i++) { 35 | 36 | sb.append(list.get(i)); 37 | 38 | if (size - 1 != i) sb.append("\n"); 39 | } 40 | 41 | return ChatUtils.format(sb.toString()); 42 | } 43 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/checks/enums/CheckType.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.checks.enums; 2 | 3 | /** 4 | * A checktype enumerations class that we'll use on our checks 5 | */ 6 | public enum CheckType { 7 | AIM("Aim", CheckCategory.COMBAT), 8 | AUTOCLICKER("AutoClicker", CheckCategory.COMBAT), 9 | BADPACKETS("BadPackets", CheckCategory.WORLD), 10 | FLY("Fly", CheckCategory.MOVEMENT), 11 | KILLAURA("KillAura", CheckCategory.COMBAT), 12 | SCAFFOLD("Scaffold", CheckCategory.WORLD), 13 | SPEED("Speed", CheckCategory.MOVEMENT), 14 | MOTION("Motion", CheckCategory.MOVEMENT), 15 | NOFALL("NoFall", CheckCategory.MOVEMENT), 16 | JESUS("Jesus", CheckCategory.MOVEMENT), 17 | VEHICLE("Vehicle", CheckCategory.MOVEMENT), 18 | ELYTRA("Elytra", CheckCategory.MOVEMENT), 19 | TIMER("Timer", CheckCategory.WORLD), 20 | OMNISPRINT("OmniSprint", CheckCategory.MOVEMENT), 21 | NOSLOW("NoSlow", CheckCategory.MOVEMENT), 22 | REACH("Reach", CheckCategory.COMBAT), 23 | VELOCITY("Velocity", CheckCategory.COMBAT), 24 | INVENTORY("Inventory", CheckCategory.WORLD), 25 | INTERACT("Interact", CheckCategory.WORLD), 26 | FASTCLIMB("FastClimb", CheckCategory.MOVEMENT), 27 | HITBOX("Hitbox", CheckCategory.COMBAT); 28 | 29 | private final String checkName; 30 | private final CheckCategory checkCategory; 31 | 32 | CheckType(String checkName, CheckCategory checkCategory) { 33 | this.checkName = checkName; 34 | this.checkCategory = checkCategory; 35 | } 36 | 37 | public String getCheckName() { 38 | return checkName; 39 | } 40 | 41 | public CheckCategory getCheckCategory() { 42 | return checkCategory; 43 | } 44 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/wrappers/WrapperPlayClientEntityAction.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.wrappers; 2 | 3 | import com.comphenix.protocol.PacketType; 4 | import com.comphenix.protocol.events.PacketContainer; 5 | import com.comphenix.protocol.reflect.StructureModifier; 6 | import com.comphenix.protocol.wrappers.EnumWrappers.PlayerAction; 7 | 8 | public class WrapperPlayClientEntityAction extends PacketWrapper { 9 | 10 | public static final PacketType TYPE = PacketType.Play.Client.ENTITY_ACTION; 11 | 12 | private final int entityId, jumpBoost; 13 | private final PlayerAction action; 14 | 15 | public WrapperPlayClientEntityAction(PacketContainer packet) { 16 | super(packet, TYPE); 17 | 18 | StructureModifier integers = handle.getIntegers(); 19 | 20 | this.entityId = integers.read(0); 21 | this.jumpBoost = integers.read(1); 22 | 23 | this.action = handle.getPlayerActions().readSafely(0); 24 | } 25 | 26 | /** 27 | * Retrieve Entity ID. 28 | *

29 | * Notes: entity's ID 30 | * 31 | * @return The current Entity ID 32 | */ 33 | public int getEntityID() { 34 | return entityId; 35 | } 36 | 37 | /** 38 | * Retrieve Action ID. 39 | *

40 | * Notes: the ID of the action, see below. 41 | * 42 | * @return The current Action ID 43 | */ 44 | public PlayerAction getAction() { 45 | return action; 46 | } 47 | 48 | /** 49 | * Retrieve Jump Boost. 50 | *

51 | * Notes: horse jump boost. Ranged from 0 -> 100. 52 | * 53 | * @return The current Jump Boost 54 | */ 55 | public int getJumpBoost() { 56 | return jumpBoost; 57 | } 58 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/wrappers/WrapperPlayClientCustomPayload.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.wrappers; 2 | 3 | import com.comphenix.protocol.PacketType; 4 | import com.comphenix.protocol.events.PacketContainer; 5 | import com.comphenix.protocol.wrappers.MinecraftKey; 6 | import io.netty.buffer.ByteBuf; 7 | import me.nik.anticheatbase.utils.ServerVersion; 8 | 9 | public class WrapperPlayClientCustomPayload extends PacketWrapper { 10 | 11 | public static final PacketType TYPE = PacketType.Play.Client.CUSTOM_PAYLOAD; 12 | 13 | public WrapperPlayClientCustomPayload(PacketContainer packet) { 14 | super(packet, TYPE); 15 | } 16 | 17 | public String getChannel() { 18 | 19 | if (ServerVersion.getVersion().isLowerThan(ServerVersion.v1_13_R1)) { 20 | 21 | return handle.getStrings().readSafely(0); 22 | 23 | } else { 24 | 25 | MinecraftKey key = handle.getMinecraftKeys().readSafely(0); 26 | 27 | if (key != null) return key.getFullKey(); 28 | } 29 | 30 | //Bad proxy configuration 31 | return null; 32 | } 33 | 34 | /** 35 | * Retrieve payload contents as a raw Netty buffer 36 | * 37 | * @return Payload contents as a Netty buffer 38 | */ 39 | public ByteBuf getContentsBuffer() { 40 | return (ByteBuf) handle.getModifier().withType(ByteBuf.class).read(0); 41 | } 42 | 43 | /** 44 | * Retrieve payload contents 45 | * 46 | * @return Payload contents as a byte array 47 | */ 48 | public byte[] getContents() { 49 | ByteBuf buffer = getContentsBuffer().copy(); 50 | byte[] array = new byte[buffer.readableBytes()]; 51 | buffer.readBytes(array); 52 | return array; 53 | } 54 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/managers/AlertManager.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.managers; 2 | 3 | import me.nik.anticheatbase.Anticheat; 4 | import org.bukkit.Bukkit; 5 | import org.bukkit.event.EventHandler; 6 | import org.bukkit.event.EventPriority; 7 | import org.bukkit.event.Listener; 8 | import org.bukkit.event.player.PlayerQuitEvent; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | import java.util.UUID; 13 | import java.util.concurrent.ExecutorService; 14 | import java.util.concurrent.Executors; 15 | 16 | /** 17 | * An alert manager class holding information about players with alerts 18 | */ 19 | public class AlertManager implements Listener, Initializer { 20 | 21 | private final ExecutorService alertExecutor = Executors.newSingleThreadExecutor(); 22 | 23 | private final List playersWithAlerts = new ArrayList<>(); 24 | 25 | @Override 26 | public void initialize() { 27 | Bukkit.getPluginManager().registerEvents(this, Anticheat.getInstance()); 28 | } 29 | 30 | public ExecutorService getAlertExecutor() { 31 | return alertExecutor; 32 | } 33 | 34 | public List getPlayersWithAlerts() { 35 | return playersWithAlerts; 36 | } 37 | 38 | public void addPlayerToAlerts(UUID uuid) { 39 | this.playersWithAlerts.add(uuid); 40 | } 41 | 42 | public void removePlayerFromAlerts(UUID uuid) { 43 | this.playersWithAlerts.remove(uuid); 44 | } 45 | 46 | public boolean hasAlerts(UUID uuid) { 47 | return this.playersWithAlerts.contains(uuid); 48 | } 49 | 50 | //Make sure we dont get a memory leak 51 | @EventHandler(priority = EventPriority.MONITOR) 52 | public void onQuit(PlayerQuitEvent e) { 53 | removePlayerFromAlerts(e.getPlayer().getUniqueId()); 54 | } 55 | 56 | @Override 57 | public void shutdown() { 58 | this.playersWithAlerts.clear(); 59 | } 60 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/utils/custom/Exempt.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.utils.custom; 2 | 3 | import me.nik.anticheatbase.managers.profile.Profile; 4 | import me.nik.anticheatbase.playerdata.data.impl.MovementData; 5 | 6 | /** 7 | * A simple class that we'll be using for exempting some checks, We'll cache the booleans every tick to 8 | * Save up some perfomance except for the ones that get updated by the server. 9 | *

10 | * This is similar to Elevated's Exempt method however instead of using Predicates 11 | * We're caching the booleans as soon as we receive a packet for maximum perfomance. 12 | *

13 | * This is a LOT faster especially when having a lot of checks, Using cached booleans instead of 14 | * Checking for example (player.getAllowFlight()) every single tick on every check. 15 | */ 16 | public class Exempt { 17 | 18 | private final Profile profile; 19 | 20 | public Exempt(Profile profile) { 21 | this.profile = profile; 22 | } 23 | 24 | private boolean movement, velocity, jesus, elytra, vehicle, autoclicker, aim; 25 | 26 | public void handleExempts(long timeStamp) { 27 | 28 | MovementData movementData = profile.getMovementData(); 29 | 30 | //Example 31 | this.movement = movementData.getDeltaXZ() == 0D && movementData.getDeltaY() == 0D; 32 | } 33 | 34 | public boolean movement() { 35 | return this.movement; 36 | } 37 | 38 | public boolean velocity() { 39 | return this.velocity; 40 | } 41 | 42 | public boolean jesus() { 43 | return this.jesus; 44 | } 45 | 46 | public boolean autoclicker() { 47 | return this.autoclicker; 48 | } 49 | 50 | public boolean aim() { 51 | return this.aim; 52 | } 53 | 54 | public boolean elytra() { 55 | return this.elytra; 56 | } 57 | 58 | public boolean vehicle() { 59 | return this.vehicle; 60 | } 61 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/wrappers/WrapperPlayClientPosition.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.wrappers; 2 | 3 | import com.comphenix.protocol.PacketType; 4 | import com.comphenix.protocol.events.PacketContainer; 5 | import com.comphenix.protocol.reflect.StructureModifier; 6 | 7 | public class WrapperPlayClientPosition extends PacketWrapper { 8 | 9 | public static final PacketType TYPE = PacketType.Play.Client.POSITION; 10 | 11 | private final double x, y, z; 12 | private final boolean onGround; 13 | 14 | public WrapperPlayClientPosition(PacketContainer packet) { 15 | super(packet, TYPE); 16 | 17 | StructureModifier doubles = handle.getDoubles(); 18 | 19 | this.x = doubles.read(0); 20 | this.y = doubles.read(1); 21 | this.z = doubles.read(2); 22 | 23 | this.onGround = handle.getBooleans().read(0); 24 | } 25 | 26 | /** 27 | * Retrieve X. 28 | *

29 | * Notes: absolute position 30 | * 31 | * @return The current X 32 | */ 33 | public double getX() { 34 | return x; 35 | } 36 | 37 | /** 38 | * Retrieve FeetY. 39 | *

40 | * Notes: absolute feet position, normally HeadY - 1.62. Used to modify the 41 | * players bounding box when going up stairs, crouching, etc… 42 | * 43 | * @return The current FeetY 44 | */ 45 | public double getY() { 46 | return y; 47 | } 48 | 49 | /** 50 | * Retrieve Z. 51 | *

52 | * Notes: absolute position 53 | * 54 | * @return The current Z 55 | */ 56 | public double getZ() { 57 | return z; 58 | } 59 | 60 | /** 61 | * Retrieve On Ground. 62 | *

63 | * Notes: true if the client is on the ground, False otherwise 64 | * 65 | * @return The current On Ground 66 | */ 67 | public boolean getOnGround() { 68 | return onGround; 69 | } 70 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/commands/subcommands/AlertsCommand.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.commands.subcommands; 2 | 3 | import me.nik.anticheatbase.Anticheat; 4 | import me.nik.anticheatbase.commands.SubCommand; 5 | import me.nik.anticheatbase.enums.MsgType; 6 | import me.nik.anticheatbase.enums.Permissions; 7 | import org.bukkit.command.CommandSender; 8 | import org.bukkit.entity.Player; 9 | 10 | import java.util.UUID; 11 | 12 | public class AlertsCommand extends SubCommand { 13 | 14 | private final Anticheat plugin; 15 | 16 | public AlertsCommand(Anticheat plugin) { 17 | this.plugin = plugin; 18 | } 19 | 20 | @Override 21 | protected String getName() { 22 | return "alerts"; 23 | } 24 | 25 | @Override 26 | protected String getDescription() { 27 | return "Toggle the alerts"; 28 | } 29 | 30 | @Override 31 | protected String getSyntax() { 32 | return "alerts"; 33 | } 34 | 35 | @Override 36 | protected String getPermission() { 37 | return Permissions.COMMAND_ALERTS.getPermission(); 38 | } 39 | 40 | @Override 41 | protected int maxArguments() { 42 | return 1; 43 | } 44 | 45 | @Override 46 | protected boolean canConsoleExecute() { 47 | return false; 48 | } 49 | 50 | @Override 51 | protected void perform(CommandSender sender, String[] args) { 52 | 53 | final UUID uuid = ((Player) sender).getUniqueId(); 54 | 55 | if (this.plugin.getAlertManager().hasAlerts(uuid)) { 56 | 57 | this.plugin.getAlertManager().removePlayerFromAlerts(uuid); 58 | 59 | sender.sendMessage(MsgType.PREFIX.getMessage() + "You have disabled the Alerts"); 60 | 61 | } else { 62 | 63 | this.plugin.getAlertManager().addPlayerToAlerts(uuid); 64 | 65 | sender.sendMessage(MsgType.PREFIX.getMessage() + "You have enabled the Alerts"); 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/managers/logs/PlayerLog.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.managers.logs; 2 | 3 | import java.text.SimpleDateFormat; 4 | import java.util.Date; 5 | 6 | public class PlayerLog { 7 | 8 | private static final String CACHED_DATE = new SimpleDateFormat("dd.MM.yyyy HH:mm").format(new Date()); 9 | 10 | private final String server; 11 | private final String player; 12 | private final String uuid; 13 | private final String check; 14 | private final String information; 15 | private final String timeStamp; 16 | 17 | public PlayerLog(String server, String player, String uuid, String check, String information) { 18 | this.server = server; 19 | this.player = player; 20 | this.uuid = uuid; 21 | this.check = check; 22 | this.information = information.length() > 50 ? information.substring(0, 50) : information; //Fixes issues with databases 23 | this.timeStamp = CACHED_DATE; 24 | } 25 | 26 | public PlayerLog(String server, String player, String uuid, String check, String information, String timeStamp) { 27 | this.server = server; 28 | this.player = player; 29 | this.uuid = uuid; 30 | this.check = check; 31 | this.information = information; 32 | this.timeStamp = timeStamp; 33 | } 34 | 35 | public String getServer() { 36 | return server; 37 | } 38 | 39 | public String getPlayer() { 40 | return player; 41 | } 42 | 43 | public String getUuid() { 44 | return uuid; 45 | } 46 | 47 | public String getCheck() { 48 | return check; 49 | } 50 | 51 | public String getInformation() { 52 | return information; 53 | } 54 | 55 | public String getTimeStamp() { 56 | return timeStamp; 57 | } 58 | 59 | @Override 60 | public String toString() { 61 | return this.server + "," 62 | + this.player + "," 63 | + this.uuid + "," 64 | + this.check + "," 65 | + this.information.replace(",", "") + "," 66 | + this.timeStamp; 67 | } 68 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/managers/logs/LogManager.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.managers.logs; 2 | 3 | import me.nik.anticheatbase.Anticheat; 4 | import me.nik.anticheatbase.files.Config; 5 | import me.nik.anticheatbase.managers.Initializer; 6 | import me.nik.anticheatbase.managers.logs.impl.FileExporter; 7 | 8 | import java.util.Queue; 9 | import java.util.concurrent.ConcurrentLinkedQueue; 10 | 11 | public class LogManager implements Initializer { 12 | 13 | private final Queue logsQueue = new ConcurrentLinkedQueue<>(); 14 | 15 | private final LogExporter logExporter; 16 | 17 | private boolean logging; 18 | 19 | public LogManager(Anticheat plugin) { 20 | 21 | switch (Config.Setting.LOGS_TYPE.getString().toLowerCase()) { 22 | 23 | /*case "mysql": 24 | 25 | this.logExporter = new MySQLExporter(plugin); 26 | 27 | break; 28 | 29 | case "sqlite": 30 | 31 | this.logExporter = new SQLiteExporter(plugin); 32 | 33 | break;*/ 34 | 35 | default: 36 | 37 | this.logExporter = new FileExporter(plugin); 38 | 39 | break; 40 | } 41 | } 42 | 43 | @Override 44 | public void initialize() { 45 | this.logExporter.initialize(); 46 | } 47 | 48 | public Queue getLogsQueue() { 49 | return this.logsQueue; 50 | } 51 | 52 | public void addLogToQueue(PlayerLog playerLog) { 53 | 54 | if (!Config.Setting.LOGS_ENABLED.getBoolean()) return; 55 | 56 | this.logsQueue.add(playerLog); 57 | } 58 | 59 | public void clearQueuedLogs() { 60 | this.logsQueue.clear(); 61 | } 62 | 63 | public LogExporter getLogExporter() { 64 | return this.logExporter; 65 | } 66 | 67 | public boolean isLogging() { 68 | return this.logging; 69 | } 70 | 71 | public void setLogging(boolean logging) { 72 | this.logging = logging; 73 | } 74 | 75 | @Override 76 | public void shutdown() { 77 | this.logsQueue.clear(); 78 | this.logExporter.shutdown(); 79 | } 80 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/utils/ServerVersion.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.utils; 2 | 3 | import org.bukkit.Bukkit; 4 | 5 | public enum ServerVersion { 6 | UNKNOWN(Integer.MAX_VALUE), 7 | v1_18_R2(18), 8 | v1_18_R1(17), 9 | v1_17_R1(16), 10 | v1_16_R3(15), 11 | v1_16_R2(14), 12 | v1_16_R1(13), 13 | v1_15_R1(12), 14 | v1_14_R1(11), 15 | v1_13_R2(10), 16 | v1_13_R1(9), 17 | v1_12_R1(8), 18 | v1_11_R1(7), 19 | v1_10_R1(6), 20 | v1_9_R2(5), 21 | v1_9_R1(4), 22 | v1_8_R3(3), 23 | v1_8_R2(2), 24 | v1_8_R1(1); 25 | 26 | /* 27 | We're going to cache this the first time we get it. 28 | */ 29 | private static ServerVersion VERSION; 30 | public final int value; 31 | 32 | ServerVersion(int value) { 33 | this.value = value; 34 | } 35 | 36 | public static ServerVersion getVersion() { 37 | 38 | if (VERSION == null) { 39 | 40 | String serverPackageName = Bukkit.getServer().getClass().getPackage().getName(); 41 | 42 | ServerVersion version; 43 | 44 | try { 45 | 46 | version = valueOf(serverPackageName.substring(serverPackageName.lastIndexOf(".") + 1).trim()); 47 | 48 | } catch (IllegalArgumentException e) { 49 | 50 | version = UNKNOWN; 51 | } 52 | 53 | VERSION = version; 54 | } 55 | 56 | return VERSION; 57 | } 58 | 59 | public boolean isHigherThan(ServerVersion target) { 60 | return VERSION.value > target.value; 61 | } 62 | 63 | public boolean isHigherThanOrEquals(ServerVersion target) { 64 | return VERSION.value >= target.value; 65 | } 66 | 67 | public boolean isLowerThan(ServerVersion target) { 68 | return VERSION.value < target.value; 69 | } 70 | 71 | public boolean isLowerThanOrEquals(ServerVersion target) { 72 | return VERSION.value <= target.value; 73 | } 74 | 75 | public boolean equals(ServerVersion target) { 76 | return VERSION.value == target.value; 77 | } 78 | 79 | @Override 80 | public String toString() { 81 | return this.name().substring(1).replace("_", "."); 82 | } 83 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/utils/custom/HitboxExpansion.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.utils.custom; 2 | 3 | import org.bukkit.entity.Entity; 4 | 5 | /* 6 | https://technical-minecraft.fandom.com/wiki/Entity_Hitbox_Sizes 7 | */ 8 | public enum HitboxExpansion { 9 | PLAYER("PLAYER", 0.6F), 10 | CREEPER("CREEPER", 0.6F), 11 | SKELETON("SKELETON", 0.6F), 12 | DOLPHIN("DOLPHIN", 0.9F), 13 | PHANTOM("PHANTOM", 0.8F), 14 | SPIDER("SPIDER", 1.4F), 15 | GIANT("GIANT", 3.6F), 16 | ZOMBIE("ZOMBIE", 0.6F), 17 | HUSK("HUSK", 0.6F), 18 | EVOKER("EVOKER", 0.6F), 19 | VINDICATOR("VINDICATOR", 0.6F), 20 | PILLAGER("PILLAGER", 0.6F), 21 | VEX("VEX", 0.4F), 22 | SLIME("SLIME", 2.04F), 23 | GHAST("GHAST", 4F), 24 | PIGLIN("PIGLIN", 0.6F), 25 | ENDERMAN("ENDERMAN", 0.6F), 26 | CAVE_SPIDER("CAVE_SPIDER", 0.7F), 27 | SILVERFISH("SILVERFISH", 0.4F), 28 | ENDERMITE("ENDERMITE", 0.4F), 29 | BLAZE("BLAZE", 0.6F), 30 | MAGMA_CUBE("MAGMA_CUBE", 2.04F), 31 | ENDER_DRAGON("ENDERDRAGON", 16F), 32 | WITHER("WITHER", 0.9F), 33 | BAT("BAT", 0.5F), 34 | WITCH("WITCH", 0.6F), 35 | GUARDIAN("GUARDIAN", 0.85F), 36 | PIG("PIG", 0.9F), 37 | SHEEP("SHEEP", 0.9F), 38 | COW("COW", 0.9F), 39 | CHICKEN("CHICKEN", 0.4F), 40 | SQUID("SQUID", 0.8F), 41 | WOLF("WOLF", 0.6F), 42 | MUSHROOM_COW("MUSHROOM_COW", 0.9F), 43 | SNOWMAN("SNOWMAN", 0.7F), 44 | CAT("CAT", 0.6F), 45 | IRON_GOLEM("IRON_GOLEM", 1.4F), 46 | HORSE("HORSE", 1.3964F), 47 | DONKEY("DONKEY", 1.3964F), 48 | RABBIT("RABBIT", 0.4F), 49 | VILLAGER("VILLAGER", 0.6F); 50 | 51 | private final float expansion; 52 | private final String name; 53 | 54 | HitboxExpansion(String name, float expansion) { 55 | this.name = name; 56 | this.expansion = expansion; 57 | } 58 | 59 | public static float getExpansion(final Entity entity) { 60 | 61 | final String entityType = entity.getType().toString(); 62 | 63 | for (HitboxExpansion expansion : values()) { 64 | 65 | if (!expansion.name.equals(entityType)) continue; 66 | 67 | return expansion.expansion; 68 | } 69 | 70 | //No expansion found 71 | return 5F; 72 | } 73 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/wrappers/WrapperPlayClientUseEntity.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.wrappers; 2 | 3 | import com.comphenix.protocol.PacketType; 4 | import com.comphenix.protocol.events.PacketContainer; 5 | import com.comphenix.protocol.wrappers.EnumWrappers; 6 | import com.comphenix.protocol.wrappers.WrappedEnumEntityUseAction; 7 | import me.nik.anticheatbase.utils.ServerVersion; 8 | import org.bukkit.util.Vector; 9 | 10 | public class WrapperPlayClientUseEntity extends PacketWrapper { 11 | 12 | public static final PacketType TYPE = PacketType.Play.Client.USE_ENTITY; 13 | 14 | private final int targetId; 15 | private final EnumWrappers.EntityUseAction type; 16 | private Vector targetVector; 17 | 18 | public WrapperPlayClientUseEntity(PacketContainer packet) { 19 | super(packet, TYPE); 20 | 21 | this.targetId = handle.getIntegers().read(0); 22 | 23 | EnumWrappers.EntityUseAction action; 24 | 25 | if (ServerVersion.getVersion().isHigherThan(ServerVersion.v1_16_R3)) { 26 | 27 | WrappedEnumEntityUseAction enumEntityUseAction = handle.getEnumEntityUseActions().read(0); 28 | 29 | if ((action = enumEntityUseAction.getAction()) == EnumWrappers.EntityUseAction.INTERACT_AT) { 30 | 31 | this.targetVector = enumEntityUseAction.getPosition(); 32 | } 33 | 34 | } else { 35 | 36 | if ((action = handle.getEntityUseActions().read(0)) == EnumWrappers.EntityUseAction.INTERACT_AT) { 37 | 38 | this.targetVector = handle.getVectors().read(0); 39 | } 40 | } 41 | 42 | 43 | this.type = action; 44 | } 45 | 46 | /** 47 | * Retrieve entity ID of the target. 48 | * 49 | * @return The current entity ID 50 | */ 51 | public int getTargetID() { 52 | return targetId; 53 | } 54 | 55 | /** 56 | * Retrieve Type. 57 | * 58 | * @return The current Type 59 | */ 60 | public EnumWrappers.EntityUseAction getType() { 61 | return type; 62 | } 63 | 64 | /** 65 | * Retrieve the target vector. 66 | * 67 | * @return The target vector or null 68 | */ 69 | public Vector getTargetVector() { 70 | return targetVector; 71 | } 72 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/wrappers/PacketWrapper.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.wrappers; 2 | 3 | import com.comphenix.protocol.PacketType; 4 | import com.comphenix.protocol.ProtocolLibrary; 5 | import com.comphenix.protocol.events.PacketContainer; 6 | import org.bukkit.entity.Player; 7 | 8 | public abstract class PacketWrapper { 9 | // The packet we will be modifying 10 | protected PacketContainer handle; 11 | 12 | /** 13 | * Constructs a new strongly typed wrapper for the given packet. 14 | * 15 | * @param handle - handle to the raw packet data. 16 | * @param type - the packet type. 17 | */ 18 | protected PacketWrapper(PacketContainer handle, PacketType type) { 19 | // Make sure we're given a valid packet 20 | if (handle == null) 21 | throw new IllegalArgumentException("Packet handle cannot be NULL."); 22 | 23 | this.handle = handle; 24 | } 25 | 26 | /** 27 | * Retrieve a handle to the raw packet data. 28 | * 29 | * @return Raw packet data. 30 | */ 31 | public PacketContainer getHandle() { 32 | return handle; 33 | } 34 | 35 | /** 36 | * Send the current packet to the given receiver. 37 | * 38 | * @param receiver - the receiver. 39 | * @throws RuntimeException If the packet cannot be sent. 40 | */ 41 | public void sendPacket(Player receiver) { 42 | try { 43 | ProtocolLibrary.getProtocolManager().sendServerPacket(receiver, 44 | getHandle()); 45 | } catch (Exception ignored) { 46 | } 47 | } 48 | 49 | /** 50 | * Send the current packet to all online players. 51 | */ 52 | public void broadcastPacket() { 53 | ProtocolLibrary.getProtocolManager().broadcastServerPacket(getHandle()); 54 | } 55 | 56 | /** 57 | * Simulate receiving the current packet from the given sender. 58 | * 59 | * @param sender - the sender. 60 | * @throws RuntimeException if the packet cannot be received. 61 | */ 62 | public void receivePacket(Player sender) { 63 | try { 64 | ProtocolLibrary.getProtocolManager().receiveClientPacket(sender, 65 | getHandle()); 66 | } catch (Exception ignored) { 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/utils/MoveUtils.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.utils; 2 | 3 | import me.nik.anticheatbase.managers.profile.Profile; 4 | 5 | /** 6 | * A simple movement utility class 7 | * NOTE: This is not perfect, It's made in case you want to make simple 8 | * Limit checks, There's lots of things that are wrong in here. 9 | */ 10 | public final class MoveUtils { 11 | 12 | //--------------------------------------------------------------------------------------- 13 | public static final float MAXIMUM_PITCH = 90.0F; 14 | //--------------------------------------------------------------------------------------- 15 | public static final float FRICTION = .91F; 16 | public static final float FRICTION_FACTOR = .6F; 17 | public static final double WATER_FRICTION = .800000011920929D; 18 | public static final double MOTION_Y_FRICTION = .9800000190734863D; 19 | public static final double JUMP_MOTION = .41999998688697815D; 20 | public static final double LAND_GROUND_MOTION = -.07840000152587834D; 21 | public static final float JUMP_MOVEMENT_FACTOR = 0.026F; 22 | //--------------------------------------------------------------------------------------- 23 | /** 24 | * Assuming they're moving forward and no acceleration is applied. 25 | */ 26 | public static final float BASE_AIR_SPEED = .3565F; 27 | /** 28 | * Assuming they're moving sideways 29 | */ 30 | public static final float BASE_GROUND_SPEED = .2867F; 31 | //--------------------------------------------------------------------------------------- 32 | /** 33 | * 1.9+ Clients last tick motion before it resets to 0 due to .003D 34 | */ 35 | public static final double RESET_MOTION = .003016261509046103D; 36 | //--------------------------------------------------------------------------------------- 37 | 38 | private MoveUtils() { 39 | } 40 | 41 | public static float getBaseAirSpeed(final Profile profile) { 42 | 43 | //Your own method here 44 | return 0F; 45 | } 46 | 47 | public static float getBaseGroundSpeed(final Profile profile) { 48 | 49 | //Your own method here 50 | return 0F; 51 | } 52 | 53 | public static float getCustomSpeed(final Profile profile) { 54 | 55 | //Your own method here 56 | return 0F; 57 | } 58 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/playerdata/processors/impl/SensitivityProcessor.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.playerdata.processors.impl; 2 | 3 | import me.nik.anticheatbase.managers.profile.Profile; 4 | import me.nik.anticheatbase.playerdata.data.impl.RotationData; 5 | import me.nik.anticheatbase.playerdata.processors.Processor; 6 | import me.nik.anticheatbase.utils.MathUtils; 7 | 8 | /** 9 | * A sensitivity processor class that we'll be using in order to hold certain data 10 | *

11 | * NOTE: This does not include a way to grab the player's sensitivity, 12 | * Feel free to add your own method since every person does it differently. 13 | */ 14 | public class SensitivityProcessor implements Processor { 15 | 16 | private final Profile profile; 17 | 18 | private double mouseX, mouseY, constantYaw, constantPitch, yawGcd, pitchGcd; 19 | 20 | public SensitivityProcessor(Profile profile) { 21 | this.profile = profile; 22 | } 23 | 24 | @Override 25 | public void process() { 26 | 27 | RotationData data = profile.getRotationData(); 28 | 29 | final float deltaYaw = data.getDeltaYaw(); 30 | final float deltaPitch = data.getDeltaPitch(); 31 | 32 | final float lastDeltaYaw = data.getLastDeltaYaw(); 33 | final float lastDeltaPitch = data.getLastDeltaPitch(); 34 | 35 | this.yawGcd = MathUtils.getAbsoluteGcd(deltaYaw, lastDeltaYaw); 36 | this.pitchGcd = MathUtils.getAbsoluteGcd(deltaPitch, lastDeltaPitch); 37 | 38 | this.constantYaw = this.yawGcd / MathUtils.EXPANDER; 39 | this.constantPitch = this.pitchGcd / MathUtils.EXPANDER; 40 | 41 | this.mouseX = (int) (deltaYaw / this.constantYaw); 42 | this.mouseY = (int) (deltaPitch / this.constantPitch); 43 | 44 | handleSensitivity(); 45 | } 46 | 47 | private void handleSensitivity() { 48 | 49 | //Your sensitivity processing here 50 | } 51 | 52 | public double getMouseX() { 53 | return mouseX; 54 | } 55 | 56 | public double getMouseY() { 57 | return mouseY; 58 | } 59 | 60 | public double getConstantYaw() { 61 | return constantYaw; 62 | } 63 | 64 | public double getConstantPitch() { 65 | return constantPitch; 66 | } 67 | 68 | public double getYawGcd() { 69 | return yawGcd; 70 | } 71 | 72 | public double getPitchGcd() { 73 | return pitchGcd; 74 | } 75 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/tasks/TickTask.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.tasks; 2 | 3 | import me.nik.anticheatbase.Anticheat; 4 | import me.nik.anticheatbase.utils.MathUtils; 5 | import org.bukkit.scheduler.BukkitRunnable; 6 | 7 | /** 8 | * A task that we'll be using in order to grab the server's state and whatnot. 9 | */ 10 | public class TickTask extends BukkitRunnable { 11 | 12 | private final Anticheat plugin; 13 | 14 | public TickTask(Anticheat plugin) { 15 | this.plugin = plugin; 16 | } 17 | 18 | private static int ticks; 19 | private static double tps = 20.0D; 20 | private static long tickTime, lastLagSpike; 21 | private int tpsTicks = 20; 22 | private long lastTime = System.currentTimeMillis(); 23 | private long currentSec; 24 | 25 | @Override 26 | public void run() { 27 | 28 | //Increment tick 29 | ticks++; 30 | 31 | //Get the current system time 32 | final long currentTime = System.currentTimeMillis(); 33 | 34 | //Handle server TPS and tick time 35 | server: 36 | { 37 | 38 | //The server's probably laggy at this early stage 39 | if (ticks < 100) break server; 40 | 41 | tickTime = currentTime - this.lastTime; 42 | 43 | this.lastTime = currentTime; 44 | 45 | final long sec = (currentTime / 1000L); 46 | 47 | if (this.currentSec == sec) { 48 | 49 | this.tpsTicks++; 50 | 51 | } else { 52 | 53 | this.currentSec = sec; 54 | 55 | tps = Math.min(MathUtils.decimalRound((tps + this.tpsTicks) / 2.0D, 2), 20.0D); 56 | 57 | this.tpsTicks = 1; 58 | } 59 | 60 | //Handle lag spikes 61 | if (tickTime >= 1050L 62 | || tps <= 14.5) { 63 | 64 | lastLagSpike = currentTime; 65 | } 66 | } 67 | 68 | //Tick 69 | this.plugin.getProfileManager().getProfileMap().values().forEach(profile -> profile.handleTick(currentTime)); 70 | } 71 | 72 | public static double getTPS() { 73 | return tps; 74 | } 75 | 76 | public static int getCurrentTick() { 77 | return Math.abs(ticks); 78 | } 79 | 80 | public static long getTickTime() { 81 | return tickTime; 82 | } 83 | 84 | public static long getLastLagSpike() { 85 | return MathUtils.elapsed(lastLagSpike); 86 | } 87 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/playerdata/processors/impl/SetbackProcessor.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.playerdata.processors.impl; 2 | 3 | import me.nik.anticheatbase.managers.profile.Profile; 4 | import me.nik.anticheatbase.playerdata.processors.Processor; 5 | import me.nik.anticheatbase.tasks.TickTask; 6 | import me.nik.anticheatbase.utils.MathUtils; 7 | import me.nik.anticheatbase.utils.TaskUtils; 8 | import me.nik.anticheatbase.utils.custom.CustomLocation; 9 | import me.nik.anticheatbase.utils.custom.SampleList; 10 | import org.bukkit.Location; 11 | import org.bukkit.block.BlockFace; 12 | import org.bukkit.entity.Player; 13 | import org.bukkit.event.player.PlayerTeleportEvent; 14 | 15 | public class SetbackProcessor implements Processor { 16 | 17 | private final SampleList locations = new SampleList<>(10, true); 18 | 19 | private final Profile profile; 20 | private int lastSetbackTicks, lastStoredLocationTicks; 21 | 22 | public SetbackProcessor(Profile profile) { 23 | this.profile = profile; 24 | } 25 | 26 | @Override 27 | public void process() { 28 | if (MathUtils.elapsedTicks(this.lastStoredLocationTicks) < 20 29 | || MathUtils.elapsedTicks(this.lastSetbackTicks) < 40) return; 30 | 31 | this.locations.add(profile.getMovementData().getLocation()); 32 | 33 | this.lastStoredLocationTicks = TickTask.getCurrentTick(); 34 | } 35 | 36 | public void setback(boolean exemptTime) { 37 | if (exemptTime && MathUtils.elapsedTicks(this.lastSetbackTicks) < 5) return; 38 | 39 | this.lastSetbackTicks = TickTask.getCurrentTick(); 40 | 41 | Player p = profile.getPlayer(); 42 | 43 | if (p == null) return; 44 | 45 | if (this.locations.isEmpty()) { 46 | 47 | final CustomLocation cloned = profile.getMovementData().getLastLocation().clone(); 48 | 49 | int count = 0; 50 | 51 | while (cloned.getBlock().getRelative(BlockFace.DOWN).isEmpty()) { 52 | 53 | cloned.subtract(0D, 1D, 0D); 54 | 55 | //Prevents crashes 56 | if (count++ > 5) break; 57 | } 58 | 59 | TaskUtils.task(() -> p.teleport(cloned.toBukkit(), PlayerTeleportEvent.TeleportCause.PLUGIN)); 60 | 61 | return; 62 | } 63 | 64 | final Location setbackLocation = locations.getLast().toBukkit(); 65 | 66 | if (setbackLocation.getWorld() != p.getWorld()) return; 67 | 68 | TaskUtils.task(() -> p.teleport(setbackLocation, PlayerTeleportEvent.TeleportCause.PLUGIN)); 69 | } 70 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/playerdata/data/impl/ActionData.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.playerdata.data.impl; 2 | 3 | import me.nik.anticheatbase.Anticheat; 4 | import me.nik.anticheatbase.managers.profile.Profile; 5 | import me.nik.anticheatbase.playerdata.data.Data; 6 | import me.nik.anticheatbase.processors.Packet; 7 | import me.nik.anticheatbase.utils.MiscUtils; 8 | import me.nik.anticheatbase.utils.custom.PlacedBlock; 9 | import me.nik.anticheatbase.utils.custom.desync.Desync; 10 | import org.bukkit.GameMode; 11 | import org.bukkit.entity.Player; 12 | import org.bukkit.inventory.ItemStack; 13 | 14 | public class ActionData implements Data { 15 | 16 | private GameMode gameMode; 17 | 18 | private boolean allowFlight, sneaking; 19 | 20 | private final Desync desync; 21 | 22 | private PlacedBlock placedBlock; 23 | 24 | private ItemStack itemInMainHand = MiscUtils.EMPTY_ITEM, itemInOffHand = MiscUtils.EMPTY_ITEM; 25 | 26 | private int lastAllowFlightTicks, lastSleepingTicks, lastRidingTicks; 27 | 28 | /* 29 | * 1.9+ 30 | */ 31 | private int lastDuplicateOnePointSeventeenPacketTicks = 100; 32 | 33 | public ActionData(Profile profile) { 34 | 35 | this.desync = new Desync(profile); 36 | 37 | //Initialize 38 | 39 | Player player = profile.getPlayer(); 40 | 41 | this.gameMode = player.getGameMode(); 42 | 43 | this.allowFlight = Anticheat.getInstance().getNmsManager().getNmsInstance().getAllowFlight(player); 44 | 45 | this.lastAllowFlightTicks = this.allowFlight ? 0 : 100; 46 | } 47 | 48 | @Override 49 | public void process(Packet packet) { 50 | /* 51 | Handle the packet 52 | */ 53 | } 54 | 55 | public int getLastRidingTicks() { 56 | return lastRidingTicks; 57 | } 58 | 59 | public PlacedBlock getPlacedBlock() { 60 | return placedBlock; 61 | } 62 | 63 | public boolean isSneaking() { 64 | return sneaking; 65 | } 66 | 67 | public ItemStack getItemInMainHand() { 68 | return itemInMainHand; 69 | } 70 | 71 | public ItemStack getItemInOffHand() { 72 | return itemInOffHand; 73 | } 74 | 75 | public Desync getDesync() { 76 | return desync; 77 | } 78 | 79 | public int getLastSleepingTicks() { 80 | return lastSleepingTicks; 81 | } 82 | 83 | public GameMode getGameMode() { 84 | return gameMode; 85 | } 86 | 87 | public int getLastDuplicateOnePointSeventeenPacketTicks() { 88 | return lastDuplicateOnePointSeventeenPacketTicks; 89 | } 90 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/wrappers/WrapperPlayClientPositionLook.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.wrappers; 2 | 3 | import com.comphenix.protocol.PacketType; 4 | import com.comphenix.protocol.events.PacketContainer; 5 | import com.comphenix.protocol.reflect.StructureModifier; 6 | 7 | public class WrapperPlayClientPositionLook extends PacketWrapper { 8 | 9 | public static final PacketType TYPE = PacketType.Play.Client.POSITION_LOOK; 10 | 11 | private final double x, y, z; 12 | private final float yaw, pitch; 13 | private final boolean onGround; 14 | 15 | public WrapperPlayClientPositionLook(PacketContainer packet) { 16 | super(packet, TYPE); 17 | 18 | StructureModifier doubles = handle.getDoubles(); 19 | StructureModifier floats = handle.getFloat(); 20 | 21 | this.x = doubles.read(0); 22 | this.y = doubles.read(1); 23 | this.z = doubles.read(2); 24 | 25 | this.yaw = floats.read(0); 26 | this.pitch = floats.read(1); 27 | 28 | this.onGround = handle.getBooleans().read(0); 29 | } 30 | 31 | /** 32 | * Retrieve X. 33 | *

34 | * Notes: absolute position 35 | * 36 | * @return The current X 37 | */ 38 | public double getX() { 39 | return x; 40 | } 41 | 42 | /** 43 | * Retrieve Feet Y. 44 | *

45 | * Notes: absolute feet position. Is normally HeadY - 1.62. Used to modify 46 | * the players bounding box when going up stairs, crouching, etc… 47 | * 48 | * @return The current FeetY 49 | */ 50 | public double getY() { 51 | return y; 52 | } 53 | 54 | /** 55 | * Retrieve Z. 56 | *

57 | * Notes: absolute position 58 | * 59 | * @return The current Z 60 | */ 61 | public double getZ() { 62 | return z; 63 | } 64 | 65 | /** 66 | * Retrieve Yaw. 67 | *

68 | * Notes: absolute rotation on the X Axis, in degrees 69 | * 70 | * @return The current Yaw 71 | */ 72 | public float getYaw() { 73 | return yaw; 74 | } 75 | 76 | /** 77 | * Retrieve Pitch. 78 | *

79 | * Notes: absolute rotation on the Y Axis, in degrees 80 | * 81 | * @return The current Pitch 82 | */ 83 | public float getPitch() { 84 | return pitch; 85 | } 86 | 87 | /** 88 | * Retrieve On Ground. 89 | *

90 | * Notes: true if the client is on the ground, False otherwise 91 | * 92 | * @return The current On Ground 93 | */ 94 | public boolean getOnGround() { 95 | return onGround; 96 | } 97 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/utils/custom/PastLocations.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.utils.custom; 2 | 3 | import org.bukkit.Location; 4 | import org.bukkit.util.Vector; 5 | 6 | import java.util.LinkedList; 7 | import java.util.List; 8 | 9 | /** 10 | * A simple tool that we're going to be using on our Reach + Hitbox Checks. 11 | */ 12 | public class PastLocations { 13 | 14 | private final ConcurrentSampleList pastLocations = new ConcurrentSampleList<>(20, true); 15 | 16 | /** 17 | * @param timeStamp Usually the player's ping 18 | * @param delta The locations to look for, Lower delta = less locations, more sensitive 19 | * @return The estimated locations 20 | */ 21 | public List getEstimatedLocationsFromPing(long currentTime, long timeStamp, long delta) { 22 | 23 | final List locations = new LinkedList<>(); 24 | 25 | final long deltaTime = currentTime - timeStamp; 26 | 27 | for (CustomLocation location : this.pastLocations) { 28 | 29 | if (Math.abs(deltaTime - location.getTimeStamp()) < delta) { 30 | 31 | locations.add(location); 32 | } 33 | } 34 | 35 | if (locations.isEmpty()) locations.add(this.pastLocations.getLast()); 36 | 37 | return locations; 38 | } 39 | 40 | /** 41 | * @param timeStamp Usually the player's ping 42 | * @param delta The locations to look for, Lower delta = less locations, more sensitive 43 | * @return The estimated locations as vectors 44 | */ 45 | public List getEstimatedVectorsFromPing(long currentTime, long timeStamp, long delta) { 46 | 47 | final List locations = new LinkedList<>(); 48 | 49 | final long deltaTime = currentTime - timeStamp; 50 | 51 | for (CustomLocation location : this.pastLocations) { 52 | 53 | if (Math.abs(deltaTime - location.getTimeStamp()) < delta) { 54 | 55 | locations.add(location.toVector()); 56 | } 57 | } 58 | 59 | if (locations.isEmpty()) locations.add(this.pastLocations.getLast().toVector()); 60 | 61 | return locations; 62 | } 63 | 64 | public boolean isCollected() { 65 | return this.pastLocations.isCollected(); 66 | } 67 | 68 | public void clear() { 69 | this.pastLocations.clear(); 70 | } 71 | 72 | public void addLocation(Location location) { 73 | this.pastLocations.add(new CustomLocation(location)); 74 | } 75 | 76 | public ConcurrentSampleList getPastLocations() { 77 | return pastLocations; 78 | } 79 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/playerdata/processors/impl/CinematicProcessor.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.playerdata.processors.impl; 2 | 3 | import me.nik.anticheatbase.managers.profile.Profile; 4 | import me.nik.anticheatbase.playerdata.data.impl.RotationData; 5 | import me.nik.anticheatbase.playerdata.processors.Processor; 6 | 7 | /** 8 | * A simple cinematic processor to determine whether a player is using cinematic 9 | *

10 | * NOTE: This is not a perfect way to handle cinematic, However i figured i should add this since 11 | * It's simple and effective. 12 | */ 13 | public class CinematicProcessor implements Processor { 14 | 15 | //This is the minimum rotation constant 16 | private static final double CINEMATIC_CONSTANT = .0078125F; 17 | 18 | private final Profile profile; 19 | private int lastCinematicTicks = 100, cinematicTicks; 20 | private boolean cinematic; 21 | 22 | public CinematicProcessor(Profile profile) { 23 | this.profile = profile; 24 | } 25 | 26 | public boolean isCinematic() { 27 | return cinematic; 28 | } 29 | 30 | @Override 31 | public void process() { 32 | 33 | //Update 34 | this.lastCinematicTicks = this.cinematic && this.cinematicTicks > 3 ? 0 : this.lastCinematicTicks + 1; 35 | 36 | RotationData data = profile.getRotationData(); 37 | 38 | final float deltaYaw = data.getDeltaYaw(); 39 | final float deltaPitch = data.getDeltaPitch(); 40 | 41 | if (deltaYaw == 0F || deltaPitch == 0F || data.getRotationsAfterTeleport() < 3) return; 42 | 43 | final float yawAccel = data.getYawAccel(); 44 | final float pitchAccel = data.getPitchAccel(); 45 | 46 | SensitivityProcessor sensitivityProcessor = data.getSensitivityProcessor(); 47 | 48 | final double constantYaw = sensitivityProcessor.getConstantYaw(); 49 | final double constantPitch = sensitivityProcessor.getConstantPitch(); 50 | 51 | final float delta = Math.abs(deltaYaw - deltaPitch); 52 | 53 | final boolean cinematic = yawAccel > .001F 54 | && yawAccel < 1F 55 | && pitchAccel > .001F 56 | && pitchAccel < 1F 57 | && delta < 3F 58 | && constantYaw < CINEMATIC_CONSTANT 59 | && constantPitch < CINEMATIC_CONSTANT; 60 | 61 | this.cinematicTicks = cinematic ? Math.min(5, this.cinematicTicks + 1) : Math.max(0, this.cinematicTicks - 1); 62 | 63 | this.cinematic = this.cinematicTicks > 1 || this.lastCinematicTicks < 80; 64 | } 65 | 66 | public int getLastCinematicTicks() { 67 | return lastCinematicTicks; 68 | } 69 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/utils/custom/Equipment.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.utils.custom; 2 | 3 | import me.nik.anticheatbase.utils.MiscUtils; 4 | import me.nik.anticheatbase.utils.ServerVersion; 5 | import org.bukkit.enchantments.Enchantment; 6 | import org.bukkit.entity.Player; 7 | import org.bukkit.inventory.ItemStack; 8 | 9 | /** 10 | * A simple equipment class holding a profile's equipment 11 | *

12 | * Surprisingly getting the player's equipment every single tick can hinder perfomance 13 | * And it does add up and make a difference on a big playercount. 14 | *

15 | * With this class we'll be getting all of them every 5 ticks since it doesn't affect us much. 16 | */ 17 | public class Equipment { 18 | 19 | private ItemStack[] armorContents = new ItemStack[3]; 20 | 21 | private int depthStriderLevel, frostWalkerLevel, soulSpeedLevel; 22 | 23 | private int ticks; 24 | 25 | public void handle(Player player) { 26 | 27 | if (this.ticks++ < 5) return; 28 | 29 | this.armorContents = player.getInventory().getArmorContents(); 30 | 31 | boots: 32 | { 33 | 34 | ItemStack boots = getBoots(); 35 | 36 | if (boots == MiscUtils.EMPTY_ITEM) break boots; 37 | 38 | this.depthStriderLevel = boots.getEnchantmentLevel(Enchantment.DEPTH_STRIDER); 39 | 40 | this.frostWalkerLevel = ServerVersion.getVersion().isLowerThan(ServerVersion.v1_13_R1) ? 0 : boots.getEnchantmentLevel(Enchantment.FROST_WALKER); 41 | 42 | this.soulSpeedLevel = ServerVersion.getVersion().isLowerThan(ServerVersion.v1_16_R1) ? 0 : boots.getEnchantmentLevel(Enchantment.SOUL_SPEED); 43 | } 44 | 45 | this.ticks = 0; 46 | } 47 | 48 | public ItemStack getBoots() { 49 | return this.armorContents[0] != null ? this.armorContents[0] : MiscUtils.EMPTY_ITEM; 50 | } 51 | 52 | public ItemStack getLeggings() { 53 | return this.armorContents[1] != null ? this.armorContents[1] : MiscUtils.EMPTY_ITEM; 54 | } 55 | 56 | public ItemStack getChestplate() { 57 | return this.armorContents[2] != null ? this.armorContents[2] : MiscUtils.EMPTY_ITEM; 58 | } 59 | 60 | public ItemStack getHelmet() { 61 | return this.armorContents[3] != null ? this.armorContents[3] : MiscUtils.EMPTY_ITEM; 62 | } 63 | 64 | public int getDepthStriderLevel() { 65 | return depthStriderLevel; 66 | } 67 | 68 | public int getFrostWalkerLevel() { 69 | return frostWalkerLevel; 70 | } 71 | 72 | public int getSoulSpeedLevel() { 73 | return soulSpeedLevel; 74 | } 75 | 76 | public ItemStack[] getArmorContents() { 77 | return armorContents; 78 | } 79 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/utils/PlayerUtils.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.utils; 2 | 3 | import me.nik.anticheatbase.managers.profile.Profile; 4 | import me.nik.anticheatbase.playerdata.data.impl.ActionData; 5 | import me.nik.anticheatbase.utils.custom.CustomLocation; 6 | import me.nik.anticheatbase.utils.versionutils.ClientVersion; 7 | import org.bukkit.inventory.ItemStack; 8 | 9 | import java.util.function.Predicate; 10 | 11 | public final class PlayerUtils { 12 | 13 | private static final Predicate blockable = item -> { 14 | switch (item.getType().toString()) { 15 | case "WOOD_SWORD": 16 | case "WOODEN_SWORD": 17 | case "STONE_SWORD": 18 | case "IRON_SWORD": 19 | case "GOLD_SWORD": 20 | case "GOLDEN_SWORD": 21 | case "DIAMOND_SWORD": 22 | case "NETHERITE_SWORD": 23 | case "SHIELD": 24 | return true; 25 | default: 26 | return false; 27 | } 28 | }; 29 | 30 | private PlayerUtils() { 31 | } 32 | 33 | public static int getMaxVelocityTicks(final double velocityXZ, final double velocityY) { 34 | 35 | int ticks = 0; 36 | 37 | float horizontal = (float) Math.abs(velocityXZ); 38 | 39 | do { 40 | 41 | horizontal -= .02F; //SpeedInAir Value 42 | 43 | horizontal *= MoveUtils.FRICTION; //Horizontal Friction 44 | 45 | if (ticks++ > 30) break; 46 | 47 | } while (horizontal > 0F); 48 | 49 | float vertical = (float) Math.abs(velocityY); 50 | 51 | do { 52 | 53 | vertical -= .08F; //Falling acceleration 54 | 55 | vertical *= MoveUtils.MOTION_Y_FRICTION; //Vertical Friction 56 | 57 | if (ticks++ > 60) break; 58 | 59 | } while (vertical > 0F); 60 | 61 | return ticks; 62 | } 63 | 64 | public static double getEyeHeight(final Profile profile) { 65 | 66 | ActionData actionData = profile.getActionData(); 67 | 68 | /* 69 | We might aswell account for it properly. 70 | */ 71 | if (actionData.getLastSleepingTicks() == 0) return .2F; 72 | 73 | float height = 1.62F; 74 | 75 | if (actionData.isSneaking()) { 76 | 77 | /* 78 | Eye height is different in 1.14+ clients. 79 | */ 80 | height -= profile.getVersion().isHigherThanOrEquals(ClientVersion.v_1_14) ? .35000002384F : .08F; 81 | } 82 | 83 | return height; 84 | } 85 | 86 | public static CustomLocation getEyeLocation(final Profile profile) { 87 | 88 | final CustomLocation location = profile.getMovementData().getLocation(); 89 | 90 | return location.clone().add(0D, getEyeHeight(profile), 0D); 91 | } 92 | 93 | public static boolean isHoldingSwordOrShield(final Profile profile) { 94 | 95 | ActionData actionData = profile.getActionData(); 96 | 97 | return blockable.test(actionData.getItemInMainHand()) || blockable.test(actionData.getItemInOffHand()); 98 | } 99 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/api/events/AnticheatViolationEvent.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.api.events; 2 | 3 | import org.bukkit.entity.Player; 4 | import org.bukkit.event.Cancellable; 5 | import org.bukkit.event.Event; 6 | import org.bukkit.event.HandlerList; 7 | 8 | public class AnticheatViolationEvent extends Event implements Cancellable { 9 | 10 | private static final HandlerList handlers = new HandlerList(); 11 | 12 | private final Player player; 13 | private final String checkName; 14 | private final String description; 15 | private final String type; 16 | private final String information; 17 | private final int vl; 18 | private final int maxVl; 19 | private final boolean experimental; 20 | private boolean cancel = false; 21 | 22 | /** 23 | * This event will always be called async, Beware. 24 | */ 25 | public AnticheatViolationEvent(Player player, String checkName, String description, String type, String information, 26 | int vl, int maxVl, boolean experimental) { 27 | super(true); 28 | this.player = player; 29 | this.checkName = checkName; 30 | this.description = description; 31 | this.type = type; 32 | this.information = information; 33 | this.vl = vl; 34 | this.maxVl = maxVl; 35 | this.experimental = experimental; 36 | } 37 | 38 | public boolean isCancelled() { 39 | return this.cancel; 40 | } 41 | 42 | public void setCancelled(boolean cancel) { 43 | this.cancel = cancel; 44 | } 45 | 46 | /** 47 | * @return The check included in this event 48 | */ 49 | public String getCheck() { 50 | return checkName; 51 | } 52 | 53 | /** 54 | * @return The check's description 55 | */ 56 | public String getDescription() { 57 | return description; 58 | } 59 | 60 | /** 61 | * @return The type of the check included in this event 62 | */ 63 | public String getType() { 64 | return type; 65 | } 66 | 67 | /** 68 | * @return The total violation amount 69 | */ 70 | public int getVl() { 71 | return vl; 72 | } 73 | 74 | /** 75 | * @return The maximum violation amount 76 | */ 77 | public int getMaxVl() { 78 | return maxVl; 79 | } 80 | 81 | /** 82 | * @return The information of why the player failed this check 83 | */ 84 | public String getInformation() { 85 | return information; 86 | } 87 | 88 | public static HandlerList getHandlerList() { 89 | return handlers; 90 | } 91 | 92 | /** 93 | * @return The player involved in this event 94 | */ 95 | public Player getPlayer() { 96 | return player; 97 | } 98 | 99 | /** 100 | * @return Whether the check is in an experimental state 101 | */ 102 | public boolean isExperimental() { 103 | return experimental; 104 | } 105 | 106 | public HandlerList getHandlers() { 107 | return handlers; 108 | } 109 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/commands/CommandManager.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.commands; 2 | 3 | import me.nik.anticheatbase.Anticheat; 4 | import me.nik.anticheatbase.commands.subcommands.AlertsCommand; 5 | import me.nik.anticheatbase.enums.MsgType; 6 | import org.bukkit.ChatColor; 7 | import org.bukkit.command.Command; 8 | import org.bukkit.command.CommandSender; 9 | import org.bukkit.command.ConsoleCommandSender; 10 | import org.bukkit.command.TabExecutor; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | import java.util.stream.Collectors; 15 | 16 | /** 17 | * A command manager class that we'll be using in order to make creating and using commands 18 | * Much easier and simple. 19 | */ 20 | public class CommandManager implements TabExecutor { 21 | 22 | private final List subCommands = new ArrayList<>(); 23 | 24 | public CommandManager(Anticheat plugin) { 25 | this.subCommands.add(new AlertsCommand(plugin)); 26 | } 27 | 28 | @Override 29 | public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { 30 | 31 | if (args.length > 0) { 32 | 33 | for (SubCommand subCommand : this.subCommands) { 34 | 35 | if (args[0].equalsIgnoreCase(subCommand.getName())) { 36 | 37 | if (!subCommand.canConsoleExecute() && sender instanceof ConsoleCommandSender) { 38 | sender.sendMessage(MsgType.CONSOLE_COMMANDS.getMessage()); 39 | return true; 40 | } 41 | 42 | if (!sender.hasPermission(subCommand.getPermission())) { 43 | sender.sendMessage(MsgType.NO_PERMISSION.getMessage()); 44 | return true; 45 | } 46 | 47 | subCommand.perform(sender, args); 48 | return true; 49 | } 50 | 51 | if (args[0].equalsIgnoreCase("help")) { 52 | helpMessage(sender); 53 | return true; 54 | } 55 | } 56 | } 57 | 58 | helpMessage(sender); 59 | return true; 60 | } 61 | 62 | @Override 63 | public List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { 64 | if (args.length == 1) { 65 | return this.subCommands.stream().map(SubCommand::getName).collect(Collectors.toList()); 66 | } 67 | 68 | return null; 69 | } 70 | 71 | private void helpMessage(CommandSender sender) { 72 | sender.sendMessage(""); 73 | sender.sendMessage(MsgType.PREFIX.getMessage() + ChatColor.WHITE + "Available Commands"); 74 | sender.sendMessage(""); 75 | 76 | this.subCommands.stream() 77 | .filter(subCommand -> sender.hasPermission(subCommand.getPermission())) 78 | .forEach(subCommand -> 79 | sender.sendMessage(ChatColor.RED + subCommand.getSyntax() + ChatColor.DARK_GRAY + " - " 80 | + ChatColor.GRAY + subCommand.getDescription())); 81 | 82 | sender.sendMessage(""); 83 | } 84 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/listeners/ClientBrandListener.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.listeners; 2 | 3 | import com.comphenix.protocol.PacketType; 4 | import com.comphenix.protocol.events.ListenerPriority; 5 | import com.comphenix.protocol.events.PacketAdapter; 6 | import com.comphenix.protocol.events.PacketEvent; 7 | import me.nik.anticheatbase.Anticheat; 8 | import me.nik.anticheatbase.managers.profile.Profile; 9 | import me.nik.anticheatbase.utils.ChatUtils; 10 | import me.nik.anticheatbase.utils.TaskUtils; 11 | import me.nik.anticheatbase.utils.custom.ExpiringSet; 12 | import me.nik.anticheatbase.wrappers.WrapperPlayClientCustomPayload; 13 | import org.bukkit.entity.Player; 14 | 15 | import java.nio.charset.StandardCharsets; 16 | import java.util.UUID; 17 | 18 | /** 19 | * A client listener that we'll use in order to get the profile's client brand. 20 | */ 21 | public class ClientBrandListener extends PacketAdapter { 22 | 23 | private final Anticheat plugin; 24 | 25 | /* 26 | We need to do this in order to fix edge cases that mostly occur in bungeecord servers 27 | Where the client brand payload would get sent more than once. 28 | */ 29 | private final ExpiringSet cache = new ExpiringSet<>(5000L); 30 | 31 | public ClientBrandListener(Anticheat plugin) { 32 | super(plugin, ListenerPriority.MONITOR, PacketType.Play.Client.CUSTOM_PAYLOAD); 33 | 34 | this.plugin = plugin; 35 | } 36 | 37 | @Override 38 | public void onPacketReceiving(PacketEvent e) { 39 | if (e.isPlayerTemporary() || e.getPlayer() == null) return; 40 | 41 | Player player = e.getPlayer(); 42 | 43 | UUID uuid = player.getUniqueId(); 44 | 45 | WrapperPlayClientCustomPayload payload = new WrapperPlayClientCustomPayload(e.getPacket()); 46 | 47 | String channel = payload.getChannel(); 48 | 49 | /* 50 | Check if we received a payload from the brand channel 51 | Or if the player has set his brand recently. 52 | */ 53 | if (channel == null || !channel.toLowerCase().endsWith("brand") || this.cache.contains(uuid)) return; 54 | 55 | String brand; 56 | 57 | try { 58 | 59 | /* 60 | Clear any color codes to make sure they're not exploiting this 61 | And translate the bytes. 62 | */ 63 | brand = ChatUtils.stripColorCodes(new String(payload.getContents(), StandardCharsets.UTF_8).substring(1)); 64 | 65 | } catch (Exception ex) { 66 | 67 | /* 68 | Cant parse, should never happen unless a client is doing it intentionally. 69 | */ 70 | return; 71 | } 72 | 73 | /* 74 | Add the player's uuid to the cache 75 | */ 76 | this.cache.add(uuid); 77 | 78 | /* 79 | Schedule it to run two seconds later to make sure the player profile has been initialized 80 | */ 81 | TaskUtils.taskLaterAsync(() -> { 82 | 83 | Profile profile = this.plugin.getProfileManager().getProfile(player); 84 | 85 | /* 86 | Just to make sure. 87 | */ 88 | if (profile == null || !profile.getClient().equals("Unknown")) return; 89 | 90 | profile.setClient(brand); 91 | 92 | }, 40L); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/utils/custom/ExpiringMap.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.utils.custom; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public class ExpiringMap extends HashMap { 7 | 8 | private final long expireMillis; 9 | 10 | private final Map> map = new HashMap<>(); 11 | 12 | public ExpiringMap(long expireMillis) { 13 | this.expireMillis = expireMillis; 14 | } 15 | 16 | @Override 17 | public V get(Object key) { 18 | 19 | validate(); 20 | 21 | for (Pair pair : this.map.values()) { 22 | 23 | if (pair.getKey() == key) return pair.getValue(); 24 | } 25 | 26 | return null; 27 | } 28 | 29 | public V removeOrDefault(Object key, V defaultValue) { 30 | 31 | validate(); 32 | 33 | Pair pair = null; 34 | 35 | for (Pair kvPair : this.map.values()) { 36 | 37 | if (kvPair.getKey() != key) continue; 38 | 39 | pair = kvPair; 40 | } 41 | 42 | if (pair != null) { 43 | 44 | this.map.values().remove(pair); 45 | 46 | return pair.getValue(); 47 | } 48 | 49 | return defaultValue; 50 | } 51 | 52 | @Override 53 | public V getOrDefault(Object key, V defaultValue) { 54 | 55 | validate(); 56 | 57 | V v = null; 58 | 59 | for (Pair pair : this.map.values()) { 60 | 61 | if (pair.getKey() != key) continue; 62 | 63 | v = pair.getValue(); 64 | } 65 | 66 | return v != null ? v : defaultValue; 67 | } 68 | 69 | @Override 70 | public V put(K key, V value) { 71 | 72 | validate(); 73 | 74 | this.map.put(System.currentTimeMillis(), new Pair<>(key, value)); 75 | 76 | return value; 77 | } 78 | 79 | @Override 80 | public V putIfAbsent(K key, V value) { 81 | 82 | validate(); 83 | 84 | V v = null; 85 | 86 | for (Pair pair : this.map.values()) { 87 | 88 | if (pair.getKey() != key) continue; 89 | 90 | v = pair.getValue(); 91 | } 92 | 93 | if (v == null) v = this.put(key, value); 94 | 95 | return v; 96 | } 97 | 98 | @Override 99 | public int size() { 100 | 101 | validate(); 102 | 103 | return this.map.size(); 104 | } 105 | 106 | @Override 107 | public boolean containsKey(Object key) { 108 | 109 | validate(); 110 | 111 | for (Pair pair : this.map.values()) { 112 | 113 | if (pair.getKey() == key) return true; 114 | } 115 | 116 | return false; 117 | } 118 | 119 | @Override 120 | public boolean containsValue(Object value) { 121 | 122 | validate(); 123 | 124 | for (Pair pair : this.map.values()) { 125 | 126 | if (pair.getValue() == value) return true; 127 | } 128 | 129 | return false; 130 | } 131 | 132 | @Override 133 | public void clear() { 134 | this.map.clear(); 135 | } 136 | 137 | private void validate() { 138 | this.map.keySet().removeIf(timeStamp -> System.currentTimeMillis() - timeStamp > this.expireMillis); 139 | } 140 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/managers/themes/ThemeManager.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.managers.themes; 2 | 3 | import me.nik.anticheatbase.Anticheat; 4 | import me.nik.anticheatbase.files.Config; 5 | import me.nik.anticheatbase.managers.Initializer; 6 | import me.nik.anticheatbase.managers.themes.impl.DefaultTheme; 7 | 8 | import java.io.File; 9 | import java.io.IOException; 10 | import java.util.ArrayList; 11 | import java.util.Arrays; 12 | import java.util.List; 13 | import java.util.concurrent.atomic.AtomicBoolean; 14 | 15 | public class ThemeManager implements Initializer { 16 | 17 | private final Anticheat plugin; 18 | 19 | private final List themes = new ArrayList<>(); 20 | 21 | private Theme theme; 22 | 23 | public ThemeManager(Anticheat plugin) { 24 | this.plugin = plugin; 25 | } 26 | 27 | @Override 28 | public void initialize() { 29 | 30 | final File folder = new File(this.plugin.getDataFolder(), "themes"); 31 | 32 | if (!folder.exists()) folder.mkdirs(); 33 | 34 | Arrays.asList( 35 | new DefaultTheme(this.plugin, "default") 36 | //More? 37 | ).forEach(BaseTheme::create); 38 | 39 | final File[] files = folder.listFiles(); 40 | 41 | if (files == null) return; 42 | 43 | Arrays.stream(files).filter(File::isFile).forEach(file -> this.themes.add(new Theme(file))); 44 | 45 | this.themes.forEach(Theme::reload); 46 | 47 | setThemeFromName(Config.Setting.THEME.getString()); 48 | 49 | final Theme defaultTheme = getThemeByName("default"); 50 | 51 | if (this.theme == null) this.theme = defaultTheme; 52 | 53 | if (this.theme != defaultTheme) { 54 | 55 | AtomicBoolean changed = new AtomicBoolean(false); 56 | 57 | defaultTheme.getConfig().getKeys(false) 58 | .stream() 59 | .filter(key -> !this.theme.getConfig().getKeys(false).contains(key)) 60 | .forEach(key -> { 61 | 62 | this.theme.getConfig().addDefault(key, defaultTheme.getConfig().get(key)); 63 | 64 | if (!changed.get()) changed.set(true); 65 | }); 66 | 67 | if (changed.get()) { 68 | 69 | try { 70 | 71 | this.theme.getConfig().save(this.theme.getFile()); 72 | 73 | this.theme.reload(); 74 | 75 | } catch (IOException e) { 76 | 77 | e.printStackTrace(); 78 | } 79 | } 80 | } 81 | } 82 | 83 | public void reload() { 84 | shutdown(); 85 | initialize(); 86 | } 87 | 88 | public void setThemeFromName(String name) { 89 | this.theme = getThemeByName(name); 90 | } 91 | 92 | public Theme getThemeByName(String name) { 93 | 94 | for (Theme theme : this.themes) { 95 | 96 | if (theme.getThemeName().equalsIgnoreCase(name)) { 97 | 98 | return theme; 99 | } 100 | } 101 | 102 | return null; 103 | } 104 | 105 | public List getThemes() { 106 | return themes; 107 | } 108 | 109 | public Theme getTheme() { 110 | return theme; 111 | } 112 | 113 | @Override 114 | public void shutdown() { 115 | this.themes.clear(); 116 | } 117 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/wrappers/WrapperPlayServerEntityVelocity.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.wrappers; 2 | 3 | import com.comphenix.protocol.PacketType; 4 | import com.comphenix.protocol.events.PacketContainer; 5 | import com.comphenix.protocol.events.PacketEvent; 6 | import org.bukkit.World; 7 | import org.bukkit.entity.Entity; 8 | 9 | public class WrapperPlayServerEntityVelocity extends PacketWrapper { 10 | public static final PacketType TYPE = 11 | PacketType.Play.Server.ENTITY_VELOCITY; 12 | 13 | public WrapperPlayServerEntityVelocity() { 14 | super(new PacketContainer(TYPE), TYPE); 15 | handle.getModifier().writeDefaults(); 16 | } 17 | 18 | public WrapperPlayServerEntityVelocity(PacketContainer packet) { 19 | super(packet, TYPE); 20 | } 21 | 22 | /** 23 | * Retrieve Entity ID. 24 | *

25 | * Notes: entity's ID 26 | * 27 | * @return The current Entity ID 28 | */ 29 | public int getEntityID() { 30 | return handle.getIntegers().read(0); 31 | } 32 | 33 | /** 34 | * Set Entity ID. 35 | * 36 | * @param value - new value. 37 | */ 38 | public void setEntityID(int value) { 39 | handle.getIntegers().write(0, value); 40 | } 41 | 42 | /** 43 | * Retrieve the entity of the painting that will be spawned. 44 | * 45 | * @param world - the current world of the entity. 46 | * @return The spawned entity. 47 | */ 48 | public Entity getEntity(World world) { 49 | return handle.getEntityModifier(world).read(0); 50 | } 51 | 52 | /** 53 | * Retrieve the entity of the painting that will be spawned. 54 | * 55 | * @param event - the packet event. 56 | * @return The spawned entity. 57 | */ 58 | public Entity getEntity(PacketEvent event) { 59 | return getEntity(event.getPlayer().getWorld()); 60 | } 61 | 62 | /** 63 | * Retrieve the velocity in the x axis. 64 | * 65 | * @return The current velocity X 66 | */ 67 | public double getVelocityX() { 68 | return handle.getIntegers().read(1) / 8000.0D; 69 | } 70 | 71 | /** 72 | * Set the velocity in the x axis. 73 | * 74 | * @param value - new value. 75 | */ 76 | public void setVelocityX(double value) { 77 | handle.getIntegers().write(1, (int) (value * 8000.0D)); 78 | } 79 | 80 | /** 81 | * Retrieve the velocity in the y axis. 82 | * 83 | * @return The current velocity y 84 | */ 85 | public double getVelocityY() { 86 | return handle.getIntegers().read(2) / 8000.0D; 87 | } 88 | 89 | /** 90 | * Set the velocity in the y axis. 91 | * 92 | * @param value - new value. 93 | */ 94 | public void setVelocityY(double value) { 95 | handle.getIntegers().write(2, (int) (value * 8000.0D)); 96 | } 97 | 98 | /** 99 | * Retrieve the velocity in the z axis. 100 | * 101 | * @return The current velocity z 102 | */ 103 | public double getVelocityZ() { 104 | return handle.getIntegers().read(3) / 8000.0D; 105 | } 106 | 107 | /** 108 | * Set the velocity in the z axis. 109 | * 110 | * @param value - new value. 111 | */ 112 | public void setVelocityZ(double value) { 113 | handle.getIntegers().write(3, (int) (value * 8000.0D)); 114 | } 115 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/utils/MathUtils.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.utils; 2 | 3 | import me.nik.anticheatbase.tasks.TickTask; 4 | import me.nik.anticheatbase.utils.fastmath.FastMath; 5 | import org.bukkit.Location; 6 | import org.bukkit.util.Vector; 7 | 8 | import java.math.BigDecimal; 9 | import java.math.RoundingMode; 10 | 11 | public final class MathUtils { 12 | 13 | private MathUtils() { 14 | } 15 | 16 | //--------------------------------------------------------------------------------------- 17 | public static final double EXPANDER = 1.6777216E7D; 18 | public static final long MINIMUM_ROTATION_DIVISOR = 131072L; 19 | //--------------------------------------------------------------------------------------- 20 | 21 | public static int millisToTicks(final long millis) { 22 | return (int) millis / 50; 23 | } 24 | 25 | public static long ticksToMillis(final int ticks) { 26 | return ticks * 50L; 27 | } 28 | 29 | public static long nanosToMillis(final long nanoseconds) { 30 | return (nanoseconds / 1000000L); 31 | } 32 | 33 | public static Vector getDirection(final Location location) { 34 | 35 | Vector vector = new Vector(); 36 | 37 | final double x = location.getYaw(); 38 | final double y = location.getPitch(); 39 | 40 | final double radiansY = FastMath.toRadians(y); 41 | 42 | vector.setY(-FastMath.sin(radiansY)); 43 | 44 | final double xz = FastMath.cos(radiansY); 45 | 46 | final double radiansRotX = FastMath.toRadians(x); 47 | 48 | vector.setX(-xz * FastMath.sin(radiansRotX)); 49 | vector.setZ(xz * FastMath.cos(radiansRotX)); 50 | 51 | return vector; 52 | } 53 | 54 | public static double decimalRound(final double val, int scale) { 55 | return BigDecimal.valueOf(val).setScale(scale, RoundingMode.HALF_EVEN).doubleValue(); 56 | } 57 | 58 | public static float strictClamp360(float value) { 59 | 60 | while (value > 360.0F) value -= 360.0F; 61 | 62 | while (value < 0.0F) value += 360.0F; 63 | 64 | return value; 65 | } 66 | 67 | public static double strictClamp360(double value) { 68 | 69 | while (value > 360.0F) value -= 360.0F; 70 | 71 | while (value < 0.0F) value += 360.0F; 72 | 73 | return value; 74 | } 75 | 76 | public static float clamp180(float value) { 77 | 78 | value %= 360F; 79 | 80 | if (value >= 180.0F) value -= 360.0F; 81 | 82 | if (value < -180.0F) value += 360.0F; 83 | 84 | return value; 85 | } 86 | 87 | public static long elapsed(final long millis) { 88 | return System.currentTimeMillis() - millis; 89 | } 90 | 91 | public static int elapsedTicks(final int ticks) { 92 | return TickTask.getCurrentTick() - ticks; 93 | } 94 | 95 | public static long getAbsoluteGcd(final float current, final float last) { 96 | 97 | final long currentExpanded = (long) (current * EXPANDER); 98 | 99 | final long lastExpanded = (long) (last * EXPANDER); 100 | 101 | return gcd(currentExpanded, lastExpanded); 102 | } 103 | 104 | private static long gcd(final long current, final long last) { 105 | return (last <= 16384L) ? current : gcd(last, current % last); 106 | } 107 | 108 | public static double getAbsoluteDelta(final double one, final double two) { 109 | return Math.abs(Math.abs(one) - Math.abs(two)); 110 | } 111 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/maven,java,intellij+all 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=maven,java,intellij+all 4 | 5 | ### Intellij+all ### 6 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 7 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 8 | 9 | # User-specific stuff 10 | .idea/**/workspace.xml 11 | .idea/**/tasks.xml 12 | .idea/**/usage.statistics.xml 13 | .idea/**/dictionaries 14 | .idea/**/shelf 15 | 16 | # AWS User-specific 17 | .idea/**/aws.xml 18 | 19 | # Generated files 20 | .idea/**/contentModel.xml 21 | 22 | # Sensitive or high-churn files 23 | .idea/**/dataSources/ 24 | .idea/**/dataSources.ids 25 | .idea/**/dataSources.local.xml 26 | .idea/**/sqlDataSources.xml 27 | .idea/**/dynamic.xml 28 | .idea/**/uiDesigner.xml 29 | .idea/**/dbnavigator.xml 30 | 31 | # Gradle 32 | .idea/**/gradle.xml 33 | .idea/**/libraries 34 | 35 | # Gradle and Maven with auto-import 36 | # When using Gradle or Maven with auto-import, you should exclude module files, 37 | # since they will be recreated, and may cause churn. Uncomment if using 38 | # auto-import. 39 | # .idea/artifacts 40 | # .idea/compiler.xml 41 | # .idea/jarRepositories.xml 42 | # .idea/modules.xml 43 | # .idea/*.iml 44 | # .idea/modules 45 | # *.iml 46 | # *.ipr 47 | 48 | # CMake 49 | cmake-build-*/ 50 | 51 | # Mongo Explorer plugin 52 | .idea/**/mongoSettings.xml 53 | 54 | # File-based project format 55 | *.iws 56 | 57 | # IntelliJ 58 | out/ 59 | 60 | # mpeltonen/sbt-idea plugin 61 | .idea_modules/ 62 | 63 | # JIRA plugin 64 | atlassian-ide-plugin.xml 65 | 66 | # Cursive Clojure plugin 67 | .idea/replstate.xml 68 | 69 | # Crashlytics plugin (for Android Studio and IntelliJ) 70 | com_crashlytics_export_strings.xml 71 | crashlytics.properties 72 | crashlytics-build.properties 73 | fabric.properties 74 | 75 | # Editor-based Rest Client 76 | .idea/httpRequests 77 | 78 | # Android studio 3.1+ serialized cache file 79 | .idea/caches/build_file_checksums.ser 80 | 81 | ### Intellij+all Patch ### 82 | # Ignores the whole .idea folder and all .iml files 83 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 84 | 85 | .idea/ 86 | 87 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 88 | 89 | *.iml 90 | modules.xml 91 | .idea/misc.xml 92 | *.ipr 93 | 94 | # Sonarlint plugin 95 | .idea/sonarlint 96 | 97 | ### Java ### 98 | # Compiled class file 99 | *.class 100 | 101 | # Log file 102 | *.log 103 | 104 | # BlueJ files 105 | *.ctxt 106 | 107 | # Mobile Tools for Java (J2ME) 108 | .mtj.tmp/ 109 | 110 | # Package Files # 111 | *.jar 112 | *.war 113 | *.nar 114 | *.ear 115 | *.zip 116 | *.tar.gz 117 | *.rar 118 | 119 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 120 | hs_err_pid* 121 | 122 | ### Maven ### 123 | target/ 124 | pom.xml.tag 125 | pom.xml.releaseBackup 126 | pom.xml.versionsBackup 127 | pom.xml.next 128 | release.properties 129 | dependency-reduced-pom.xml 130 | buildNumber.properties 131 | .mvn/timing.properties 132 | # https://github.com/takari/maven-wrapper#usage-without-binary-jar 133 | .mvn/wrapper/maven-wrapper.jar 134 | 135 | ### Maven Patch ### 136 | # Eclipse m2e generated files 137 | # Eclipse Core 138 | .project 139 | # JDT-specific (Eclipse Java Development Tools) 140 | .classpath 141 | 142 | # End of https://www.toptal.com/developers/gitignore/api/maven,java,intellij+all 143 | -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/utils/minecraft/Vec3i.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.utils.minecraft; 2 | 3 | public class Vec3i implements Comparable { 4 | 5 | /** 6 | * The Null vector constant (0, 0, 0) 7 | */ 8 | public static final Vec3i NULL_VECTOR = new Vec3i(0, 0, 0); 9 | 10 | /** 11 | * X coordinate 12 | */ 13 | private final int x; 14 | 15 | /** 16 | * Y coordinate 17 | */ 18 | private final int y; 19 | 20 | /** 21 | * Z coordinate 22 | */ 23 | private final int z; 24 | 25 | public Vec3i(int xIn, int yIn, int zIn) { 26 | this.x = xIn; 27 | this.y = yIn; 28 | this.z = zIn; 29 | } 30 | 31 | public Vec3i(double xIn, double yIn, double zIn) { 32 | this(MathHelper.floor_double(xIn), MathHelper.floor_double(yIn), MathHelper.floor_double(zIn)); 33 | } 34 | 35 | public boolean equals(Object p_equals_1_) { 36 | if (this == p_equals_1_) { 37 | return true; 38 | } else if (!(p_equals_1_ instanceof Vec3i)) { 39 | return false; 40 | } else { 41 | Vec3i vec3i = (Vec3i) p_equals_1_; 42 | return this.getX() == vec3i.getX() && (this.getY() == vec3i.getY() && this.getZ() == vec3i.getZ()); 43 | } 44 | } 45 | 46 | public int hashCode() { 47 | return (this.getY() + this.getZ() * 31) * 31 + this.getX(); 48 | } 49 | 50 | public int compareTo(Vec3i p_compareTo_1_) { 51 | return this.getY() == p_compareTo_1_.getY() ? (this.getZ() == p_compareTo_1_.getZ() ? this.getX() - 52 | p_compareTo_1_.getX() : this.getZ() - p_compareTo_1_.getZ()) : this.getY() - p_compareTo_1_.getY(); 53 | } 54 | 55 | /** 56 | * Get the X coordinate 57 | */ 58 | public int getX() { 59 | return this.x; 60 | } 61 | 62 | /** 63 | * Get the Y coordinate 64 | */ 65 | public int getY() { 66 | return this.y; 67 | } 68 | 69 | /** 70 | * Get the Z coordinate 71 | */ 72 | public int getZ() { 73 | return this.z; 74 | } 75 | 76 | /** 77 | * Calculate the cross product of this and the given Vector 78 | */ 79 | public Vec3i crossProduct(Vec3i vec) { 80 | return new Vec3i(this.getY() * vec.getZ() - this.getZ() * vec.getY(), this.getZ() * vec.getX() 81 | - this.getX() * vec.getZ(), this.getX() * vec.getY() - this.getY() * vec.getX()); 82 | } 83 | 84 | /** 85 | * Calculate squared distance to the given coordinates 86 | */ 87 | public double distanceSq(double toX, double toY, double toZ) { 88 | double d0 = (double) this.getX() - toX; 89 | double d1 = (double) this.getY() - toY; 90 | double d2 = (double) this.getZ() - toZ; 91 | return d0 * d0 + d1 * d1 + d2 * d2; 92 | } 93 | 94 | /** 95 | * Compute square of distance from point x, y, z to center of this Block 96 | */ 97 | public double distanceSqToCenter(double xIn, double yIn, double zIn) { 98 | double d0 = (double) this.getX() + 0.5D - xIn; 99 | double d1 = (double) this.getY() + 0.5D - yIn; 100 | double d2 = (double) this.getZ() + 0.5D - zIn; 101 | return d0 * d0 + d1 * d1 + d2 * d2; 102 | } 103 | 104 | /** 105 | * Calculate squared distance to the given Vector 106 | */ 107 | public double distanceSq(Vec3i to) { 108 | return this.distanceSq(to.getX(), to.getY(), to.getZ()); 109 | } 110 | 111 | public String toString() { 112 | return this.x + " " + this.y + " " + this.z; 113 | } 114 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/nms/InstanceDefault.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.nms; 2 | 3 | import me.nik.anticheatbase.utils.ServerVersion; 4 | import org.bukkit.Material; 5 | import org.bukkit.World; 6 | import org.bukkit.attribute.Attribute; 7 | import org.bukkit.block.Block; 8 | import org.bukkit.entity.Entity; 9 | import org.bukkit.entity.Player; 10 | import org.bukkit.inventory.ItemStack; 11 | 12 | public class InstanceDefault implements NmsInstance { 13 | 14 | @Override 15 | public float getAttackCooldown(Player player) { 16 | return ServerVersion.getVersion().isHigherThanOrEquals(ServerVersion.v1_16_R1) ? player.getAttackCooldown() : 1F; 17 | } 18 | 19 | @Override 20 | public boolean isChunkLoaded(World world, int x, int z) { 21 | return world.isChunkLoaded(x >> 4, z >> 4); 22 | } 23 | 24 | @Override 25 | public Material getType(Block block) { 26 | return block.getType(); 27 | } 28 | 29 | @Override 30 | public Entity[] getChunkEntities(World world, int x, int z) { 31 | return world.isChunkLoaded(x >> 4, z >> 4) ? world.getChunkAt(x >> 4, z >> 4).getEntities() : new Entity[0]; 32 | } 33 | 34 | @Override 35 | public boolean isWaterLogged(Block block) { 36 | return ServerVersion.getVersion().isHigherThanOrEquals(ServerVersion.v1_13_R1) 37 | && (block.getBlockData() instanceof org.bukkit.block.data.Waterlogged 38 | && ((org.bukkit.block.data.Waterlogged) block).isWaterlogged()); 39 | } 40 | 41 | @Override 42 | public boolean isDead(Player player) { 43 | return player.isDead(); 44 | } 45 | 46 | @Override 47 | public boolean isSleeping(Player player) { 48 | return player.isSleeping(); 49 | } 50 | 51 | @Override 52 | public boolean isGliding(Player player) { 53 | return ServerVersion.getVersion().isHigherThan(ServerVersion.v1_8_R3) && player.isGliding(); 54 | } 55 | 56 | @Override 57 | public boolean isInsideVehicle(Player player) { 58 | return player.isInsideVehicle(); 59 | } 60 | 61 | @Override 62 | public boolean isRiptiding(Player player) { 63 | return ServerVersion.getVersion().isHigherThanOrEquals(ServerVersion.v1_13_R1) && player.isRiptiding(); 64 | } 65 | 66 | @Override 67 | public boolean isBlocking(Player player) { 68 | return player.isBlocking(); 69 | } 70 | 71 | @Override 72 | public boolean isSneaking(Player player) { 73 | return player.isSneaking(); 74 | } 75 | 76 | @Override 77 | public ItemStack getItemInMainHand(Player player) { 78 | return player.getItemInHand(); 79 | } 80 | 81 | @Override 82 | public ItemStack getItemInOffHand(Player player) { 83 | return ServerVersion.getVersion().isHigherThan(ServerVersion.v1_8_R3) ? player.getInventory().getItemInOffHand() : null; 84 | } 85 | 86 | @Override 87 | public float getWalkSpeed(Player player) { 88 | return player.getWalkSpeed(); 89 | } 90 | 91 | @Override 92 | public float getAttributeSpeed(Player player) { 93 | return ServerVersion.getVersion().isHigherThan(ServerVersion.v1_8_R3) ? (float) player.getAttribute(Attribute.GENERIC_MOVEMENT_SPEED).getValue() : 0F; 94 | } 95 | 96 | @Override 97 | public boolean getAllowFlight(Player player) { 98 | return player.getAllowFlight(); 99 | } 100 | 101 | @Override 102 | public boolean isFlying(Player player) { 103 | return player.isFlying(); 104 | } 105 | 106 | @Override 107 | public float getFallDistance(Player player) { 108 | return player.getFallDistance(); 109 | } 110 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/utils/custom/desync/Desync.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.utils.custom.desync; 2 | 3 | import me.nik.anticheatbase.Anticheat; 4 | import me.nik.anticheatbase.managers.profile.Profile; 5 | import me.nik.anticheatbase.tasks.TickTask; 6 | import me.nik.anticheatbase.utils.MathUtils; 7 | import org.bukkit.Bukkit; 8 | import org.bukkit.entity.Player; 9 | import org.bukkit.event.inventory.InventoryType; 10 | import org.bukkit.inventory.Inventory; 11 | import org.bukkit.inventory.PlayerInventory; 12 | import org.bukkit.scheduler.BukkitRunnable; 13 | 14 | /** 15 | * A simple class that is going to help us handle and fix any type of desync with the client 16 | */ 17 | public class Desync { 18 | 19 | private static final Inventory CACHED_INVENTORY = Bukkit.createInventory(null, InventoryType.PLAYER); 20 | private final Profile profile; 21 | private int lastFixedTicks; 22 | 23 | public Desync(Profile profile) { 24 | this.profile = profile; 25 | } 26 | 27 | public void fix(DesyncType desyncType) { 28 | 29 | //Make sure this method isn't being spammed by any check that hasn't updated its status yet 30 | if (MathUtils.elapsedTicks(this.lastFixedTicks) < 15) return; 31 | 32 | final Player player = profile.getPlayer(); 33 | 34 | switch (desyncType) { 35 | 36 | case BLOCKING: 37 | 38 | final PlayerInventory inventory = player.getInventory(); 39 | 40 | final int currentSlot = inventory.getHeldItemSlot(); 41 | 42 | final int nextSlot = currentSlot == 0 ? currentSlot + 1 : currentSlot - 1; 43 | 44 | inventory.setHeldItemSlot(nextSlot); 45 | 46 | break; 47 | 48 | case SNEAKING: 49 | case SPRINTING: 50 | 51 | /* 52 | We need to do this on the main thread due to async catchers 53 | The reason we're doing it one by one after a tick 54 | Is due to certain clients not properly un-sneaking, un-sprinting 55 | If this gets executed instantly. 56 | */ 57 | new BukkitRunnable() { 58 | 59 | int state = 0; 60 | 61 | @Override 62 | public void run() { 63 | 64 | switch (state++) { 65 | 66 | case 1: 67 | 68 | /* 69 | Close the inventory first to make sure they're not using any type 70 | Of inventory move alongside noslow. 71 | */ 72 | player.closeInventory(); 73 | 74 | break; 75 | 76 | case 2: 77 | 78 | /* 79 | Open an empty inventory in order to force them 80 | To un-sprint and un-sneak. 81 | */ 82 | player.openInventory(CACHED_INVENTORY); 83 | 84 | break; 85 | 86 | case 3: 87 | 88 | /* 89 | Close the inventory and cancel the task 90 | */ 91 | player.closeInventory(); 92 | 93 | this.cancel(); 94 | 95 | break; 96 | } 97 | } 98 | }.runTaskTimer(Anticheat.getInstance(), 0, 0); 99 | 100 | break; 101 | } 102 | 103 | this.lastFixedTicks = TickTask.getCurrentTick(); 104 | } 105 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/listeners/ViolationListener.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.listeners; 2 | 3 | import me.nik.anticheatbase.Anticheat; 4 | import me.nik.anticheatbase.api.events.AnticheatViolationEvent; 5 | import me.nik.anticheatbase.enums.MsgType; 6 | import me.nik.anticheatbase.files.Config; 7 | import me.nik.anticheatbase.managers.logs.PlayerLog; 8 | import me.nik.anticheatbase.managers.profile.Profile; 9 | import me.nik.anticheatbase.tasks.TickTask; 10 | import me.nik.anticheatbase.utils.ChatUtils; 11 | import me.nik.anticheatbase.utils.JsonBuilder; 12 | import org.bukkit.Bukkit; 13 | import org.bukkit.entity.Player; 14 | import org.bukkit.event.EventHandler; 15 | import org.bukkit.event.EventPriority; 16 | import org.bukkit.event.Listener; 17 | 18 | /** 19 | * A violation listener that we'll use for our alerts by using our custom event. 20 | */ 21 | public class ViolationListener implements Listener { 22 | 23 | private final Anticheat plugin; 24 | 25 | public ViolationListener(Anticheat plugin) { 26 | this.plugin = plugin; 27 | } 28 | 29 | @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) 30 | public void onViolation(AnticheatViolationEvent e) { 31 | 32 | this.plugin.getAlertManager().getAlertExecutor().execute(() -> { 33 | 34 | final Player p = e.getPlayer(); 35 | 36 | if (p == null || !p.isOnline()) return; 37 | 38 | Profile profile = this.plugin.getProfileManager().getProfile(p); 39 | 40 | if (profile == null) return; 41 | 42 | final String tps = String.valueOf(TickTask.getTPS()); 43 | 44 | final String checkType = e.getType(); 45 | 46 | final String checkName = e.getCheck(); 47 | 48 | final String check = (checkType.isEmpty() ? checkName : checkName + " (" + checkType + ")") 49 | + (e.isExperimental() ? " (Experimental)" : ""); 50 | 51 | final String description = e.getDescription(); 52 | 53 | final String information = e.getInformation(); 54 | 55 | final String playerName = p.getName(); 56 | 57 | final int vl = e.getVl(); 58 | 59 | this.plugin.getLogManager().addLogToQueue(new PlayerLog( 60 | Config.Setting.SERVER_NAME.getString(), 61 | playerName, 62 | p.getUniqueId().toString(), 63 | check, 64 | information 65 | )); 66 | 67 | //We're sending the alerts by using the server chat packet, Making this much more efficient. 68 | alerts: 69 | { 70 | 71 | final String hoverMessage = MsgType.ALERT_HOVER.getMessage() 72 | .replace("%description%", description) 73 | .replace("%information%", information) 74 | .replace("%tps%", tps); 75 | 76 | final String alertMessage = MsgType.ALERT_MESSAGE.getMessage() 77 | .replace("%player%", playerName) 78 | .replace("%check%", check) 79 | .replace("%vl%", String.valueOf(vl)); 80 | 81 | JsonBuilder jsonBuilder = new JsonBuilder(alertMessage) 82 | .setHoverEvent(JsonBuilder.HoverEventType.SHOW_TEXT, hoverMessage) 83 | .setClickEvent(JsonBuilder.ClickEventType.RUN_COMMAND, "/tp " + playerName) 84 | .buildText(); 85 | 86 | jsonBuilder.sendMessage(this.plugin.getAlertManager().getPlayersWithAlerts()); 87 | 88 | if (!Config.Setting.CHECK_SETTINGS_ALERT_CONSOLE.getBoolean()) break alerts; 89 | 90 | //We're using bukkit's logger in order for the prefix to not show twice 91 | Bukkit.getLogger().info(ChatUtils.stripColorCodes(alertMessage)); 92 | } 93 | }); 94 | } 95 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/managers/threads/ThreadManager.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.managers.threads; 2 | 3 | import me.nik.anticheatbase.Anticheat; 4 | import me.nik.anticheatbase.managers.Initializer; 5 | import me.nik.anticheatbase.managers.profile.Profile; 6 | import me.nik.anticheatbase.utils.MiscUtils; 7 | import me.nik.anticheatbase.utils.custom.exception.AnticheatException; 8 | import org.bukkit.Bukkit; 9 | import org.bukkit.event.EventHandler; 10 | import org.bukkit.event.EventPriority; 11 | import org.bukkit.event.Listener; 12 | import org.bukkit.event.player.PlayerQuitEvent; 13 | 14 | import java.util.ArrayList; 15 | import java.util.Comparator; 16 | import java.util.List; 17 | 18 | /** 19 | * A simple thread manager class that'll help us make sure a player profile is provided with the best 20 | * Available thread at any time, While also shutting down threads that are not used. 21 | */ 22 | public class ThreadManager implements Listener, Initializer { 23 | 24 | //Get a proper thread limit 25 | private static final int MAX_THREADS = Runtime.getRuntime().availableProcessors() * 2; 26 | 27 | private final List profileThreads = new ArrayList<>(); 28 | 29 | private final Anticheat plugin; 30 | 31 | public ThreadManager(Anticheat plugin) { 32 | this.plugin = plugin; 33 | } 34 | 35 | @Override 36 | public void initialize() { 37 | Bukkit.getPluginManager().registerEvents(this, Anticheat.getInstance()); 38 | } 39 | 40 | public ProfileThread getAvailableProfileThread() { 41 | 42 | ProfileThread profileThread; 43 | 44 | //Check whether or not we should create a new thread based on the thread limit 45 | if (this.profileThreads.size() < MAX_THREADS) { 46 | 47 | //Create a new profile thread and set it to our variable in order to use it 48 | profileThread = new ProfileThread(); 49 | 50 | //Add our new profile thread to the list in order to use it for future profiles 51 | this.profileThreads.add(profileThread); 52 | 53 | } else { 54 | 55 | //Get an available thread based on the profiles using it, Otherwise grab a random element to avoid issues. 56 | profileThread = this.profileThreads 57 | .stream() 58 | .min(Comparator.comparing(ProfileThread::getProfileCount)) 59 | .orElse(MiscUtils.randomElement(this.profileThreads)); 60 | } 61 | 62 | //Throw an exception if the profile thread is null, Which should be impossible. 63 | if (profileThread == null) { 64 | 65 | throw new AnticheatException("Encountered a null profile thread, Please restart the server to avoid any issues."); 66 | } 67 | 68 | //Return the available thread and increment the profile count 69 | return profileThread.incrementAndGet(); 70 | } 71 | 72 | @EventHandler(priority = EventPriority.LOWEST) 73 | public void onQuit(PlayerQuitEvent e) { 74 | 75 | Profile profile = this.plugin.getProfileManager().getProfile(e.getPlayer()); 76 | 77 | if (profile == null) return; 78 | 79 | ProfileThread profileThread = profile.getProfileThread(); 80 | 81 | /* 82 | If this is the only profile using this thread, Shut it down and remove it from the list 83 | Otherwise decrease the counter and return. 84 | */ 85 | if (profileThread.getProfileCount() > 1) { 86 | 87 | profileThread.decrement(); 88 | 89 | return; 90 | } 91 | 92 | this.profileThreads.remove(profileThread.shutdownThread()); 93 | } 94 | 95 | @Override 96 | public void shutdown() { 97 | this.profileThreads.forEach(ProfileThread::shutdownThread); 98 | 99 | this.profileThreads.clear(); 100 | } 101 | } -------------------------------------------------------------------------------- /dependency-reduced-pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | me.nik 5 | AnticheatBase 6 | AnticheatBase 7 | 1.0.0 8 | A clean anticheat base 9 | 10 | clean install package 11 | 12 | 13 | true 14 | src/main/resources 15 | 16 | 17 | ${project.name} 18 | 19 | 20 | maven-shade-plugin 21 | 3.2.1 22 | 23 | 24 | package 25 | 26 | shade 27 | 28 | 29 | 30 | 31 | 32 | 33 | io.github.classgraph 34 | 35 | 36 | 37 | 38 | io.github.classgraph 39 | anticheat_libs.classgraph 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | spigotmc-repo 49 | https://hub.spigotmc.org/nexus/content/repositories/snapshots/ 50 | 51 | 52 | dmulloy2-repo 53 | https://repo.dmulloy2.net/nexus/repository/public/ 54 | 55 | 56 | jitpack.io 57 | https://jitpack.io 58 | 59 | 60 | 61 | 62 | org.spigotmc 63 | spigot-api 64 | 1.17.1-R0.1-SNAPSHOT 65 | provided 66 | 67 | 68 | commons-lang 69 | commons-lang 70 | 71 | 72 | guava 73 | com.google.guava 74 | 75 | 76 | gson 77 | com.google.code.gson 78 | 79 | 80 | bungeecord-chat 81 | net.md-5 82 | 83 | 84 | snakeyaml 85 | org.yaml 86 | 87 | 88 | 89 | 90 | com.comphenix.protocol 91 | ProtocolLib 92 | 4.7.0 93 | provided 94 | 95 | 96 | byte-buddy 97 | net.bytebuddy 98 | 99 | 100 | 101 | 102 | 103 | UTF-8 104 | UTF-8 105 | 8 106 | 8 107 | 108 | 109 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | me.nik 8 | AnticheatBase 9 | 1.0.0 10 | 11 | 12 | 8 13 | 8 14 | UTF-8 15 | UTF-8 16 | 17 | 18 | AnticheatBase 19 | 20 | A simple and efficient anticheat base 21 | 22 | 23 | 24 | 25 | bungeecord-repo 26 | https://oss.sonatype.org/content/repositories/snapshots 27 | 28 | 29 | 30 | spigotmc-repo 31 | https://hub.spigotmc.org/nexus/content/repositories/snapshots/ 32 | 33 | 34 | 35 | dmulloy2-repo 36 | https://repo.dmulloy2.net/nexus/repository/public/ 37 | 38 | 39 | 40 | viaversion-repo 41 | https://repo.viaversion.com 42 | 43 | 44 | 45 | jitpack.io 46 | https://jitpack.io 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | org.spigotmc 55 | spigot-api 56 | 1.20.2-R0.1-SNAPSHOT 57 | provided 58 | 59 | 60 | 61 | com.comphenix.protocol 62 | ProtocolLib 63 | 5.0.0 64 | provided 65 | 66 | 67 | 68 | us.myles 69 | viaversion 70 | LATEST 71 | provided 72 | 73 | 74 | 75 | com.github.ProtocolSupport 76 | ProtocolSupport 77 | master-04834effe2-1 78 | provided 79 | 80 | 81 | 82 | net.md-5 83 | bungeecord-api 84 | 1.20-R0.1-SNAPSHOT 85 | provided 86 | 87 | 88 | 89 | 90 | 91 | ${project.name} 92 | clean install package 93 | 94 | 95 | src/main/resources 96 | true 97 | 98 | 99 | 100 | 101 | 102 | org.apache.maven.plugins 103 | maven-shade-plugin 104 | 3.2.1 105 | 106 | 107 | package 108 | 109 | shade 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/utils/custom/CheckHolder.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.utils.custom; 2 | 3 | import me.nik.anticheatbase.checks.annotations.Testing; 4 | import me.nik.anticheatbase.checks.impl.speed.SpeedA; 5 | import me.nik.anticheatbase.checks.types.Check; 6 | import me.nik.anticheatbase.managers.profile.Profile; 7 | import me.nik.anticheatbase.processors.Packet; 8 | 9 | import java.util.Arrays; 10 | 11 | public class CheckHolder { 12 | 13 | private final Profile profile; 14 | private Check[] checks; 15 | private int checksSize; 16 | private boolean testing; //Used for testing new checks 17 | 18 | public CheckHolder(Profile profile) { 19 | this.profile = profile; 20 | } 21 | 22 | public void runChecks(Packet packet) { 23 | /* 24 | Fastest way to loop through many objects, If you think this is stupid 25 | Then benchmark the long term perfomance yourself with many profilers and java articles. 26 | */ 27 | for (int i = 0; i < this.checksSize; i++) this.checks[i].handle(packet); 28 | } 29 | 30 | public void registerAll() { 31 | 32 | /* 33 | * Check initialization 34 | */ 35 | addChecks( 36 | 37 | /* 38 | Speed 39 | */ 40 | new SpeedA(this.profile) 41 | ); 42 | 43 | /* 44 | Remove checks if a testing check is present. 45 | */ 46 | testing: 47 | { 48 | 49 | /* 50 | Testing check not present, break. 51 | */ 52 | if (!this.testing) break testing; 53 | 54 | /* 55 | Remove the rest of the checks since a testing check is present. 56 | */ 57 | this.checks = Arrays.stream(this.checks) 58 | .filter(check -> check.getClass().isAnnotationPresent(Testing.class)) 59 | .toArray(Check[]::new); 60 | 61 | /* 62 | Update the size since we're only going to be running one check. 63 | */ 64 | this.checksSize = 1; 65 | } 66 | } 67 | 68 | private void addChecks(Check... checks) { 69 | 70 | /* 71 | Create a new check array to account for reloads. 72 | */ 73 | this.checks = new Check[0]; 74 | 75 | /* 76 | Reset the check size to account for reloads 77 | */ 78 | this.checksSize = 0; 79 | 80 | /* 81 | Loop through the input checks 82 | */ 83 | for (Check check : checks) { 84 | 85 | /* 86 | Check if this is being used by a GUI, where we put null as the profile 87 | Or a check with the @Testing annotation is present or disabled. 88 | */ 89 | if (this.profile != null && (!check.isEnabled() || isTesting(check))) continue; 90 | 91 | /* 92 | Copy the original array and increment the size just like an ArrayList. 93 | */ 94 | this.checks = Arrays.copyOf(this.checks, this.checksSize + 1); 95 | 96 | /* 97 | Update the check. 98 | */ 99 | this.checks[this.checksSize] = check; 100 | 101 | /* 102 | Update the check size variable for improved looping perfomance 103 | */ 104 | this.checksSize++; 105 | } 106 | } 107 | 108 | /** 109 | * If a check with the testing annotation is present, It'll set the testing boolean to true, load it and then 110 | * Prevent any other checks from registering. 111 | */ 112 | private boolean isTesting(Check check) { 113 | 114 | if (this.testing) return true; 115 | 116 | /* 117 | Update the variable and return false in order to register this check 118 | But not the next ones. 119 | */ 120 | if (check.getClass().isAnnotationPresent(Testing.class)) this.testing = true; 121 | 122 | return false; 123 | } 124 | 125 | public Check[] getChecks() { 126 | return checks; 127 | } 128 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/files/commentedfiles/CommentedFileConfiguration.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.files.commentedfiles; 2 | 3 | import org.bukkit.configuration.file.YamlConfiguration; 4 | import org.bukkit.plugin.java.JavaPlugin; 5 | import org.yaml.snakeyaml.DumperOptions; 6 | 7 | import java.io.File; 8 | import java.io.Reader; 9 | import java.lang.reflect.Field; 10 | import java.util.stream.Stream; 11 | 12 | public class CommentedFileConfiguration extends CommentedConfigurationSection { 13 | 14 | private int comments; 15 | private final CommentedFileConfigurationHelper helper; 16 | private final File file; 17 | 18 | public CommentedFileConfiguration(Reader configStream, File configFile, int comments, JavaPlugin plugin) { 19 | super(YamlConfiguration.loadConfiguration(configStream)); 20 | this.comments = comments; 21 | this.helper = new CommentedFileConfigurationHelper(plugin); 22 | this.file = configFile; 23 | } 24 | 25 | public static CommentedFileConfiguration loadConfiguration(JavaPlugin plugin, File file) { 26 | return new CommentedFileConfigurationHelper(plugin).getNewConfig(file); 27 | } 28 | 29 | public void set(String path, Object value, String... comments) { 30 | if (!this.contains(path)) { 31 | int subpathIndex = path.lastIndexOf('.'); 32 | String subpath = subpathIndex == -1 ? "" : path.substring(0, subpathIndex) + '.'; 33 | 34 | for (String comment : comments) { 35 | this.set(subpath + this.helper.getPluginName() + "_COMMENT_" + this.comments, " " + comment); 36 | this.comments++; 37 | } 38 | } 39 | 40 | this.set(path, value); 41 | } 42 | 43 | public void addComments(String... comments) { 44 | for (String comment : comments) { 45 | this.set(this.helper.getPluginName() + "_COMMENT_" + this.comments, " " + comment); 46 | this.comments++; 47 | } 48 | } 49 | 50 | public void reloadConfig() { 51 | this.config = YamlConfiguration.loadConfiguration(this.helper.getConfigContent(this.file)); 52 | } 53 | 54 | public void save() { 55 | this.save(false); 56 | } 57 | 58 | public void save(boolean compactLines) { 59 | String config = this.getConfigAsString(); 60 | this.helper.saveConfig(config, this.file, compactLines); 61 | } 62 | 63 | public void save(File file) { 64 | this.save(file, false); 65 | } 66 | 67 | public void save(File file, boolean compactLines) { 68 | String config = this.getConfigAsString(); 69 | this.helper.saveConfig(config, file, compactLines); 70 | } 71 | 72 | private String getConfigAsString() { 73 | if (!(this.config instanceof YamlConfiguration)) 74 | throw new UnsupportedOperationException("Cannot get config string of non-YamlConfiguration"); 75 | 76 | YamlConfiguration yamlConfiguration = (YamlConfiguration) this.config; 77 | 78 | // Edit the configuration to how we want it 79 | try { 80 | Field field_yamlOptions; 81 | try { 82 | field_yamlOptions = YamlConfiguration.class.getDeclaredField("yamlOptions"); 83 | } catch (NoSuchFieldException e) { // This is used for 1.18.1+ 84 | field_yamlOptions = YamlConfiguration.class.getDeclaredField("yamlDumperOptions"); 85 | } 86 | 87 | field_yamlOptions.setAccessible(true); 88 | DumperOptions yamlOptions = (DumperOptions) field_yamlOptions.get(yamlConfiguration); 89 | yamlOptions.setWidth(Integer.MAX_VALUE); 90 | 91 | if (Stream.of(DumperOptions.class.getDeclaredMethods()).anyMatch(x -> x.getName().equals("setIndicatorIndent"))) 92 | yamlOptions.setIndicatorIndent(2); 93 | 94 | if (Stream.of(DumperOptions.class.getDeclaredMethods()).anyMatch(x -> x.getName().equals("setProcessComments"))) 95 | yamlOptions.setProcessComments(false); 96 | 97 | if (Stream.of(DumperOptions.class.getDeclaredMethods()).anyMatch(x -> x.getName().equals("setSplitLines"))) 98 | yamlOptions.setSplitLines(false); 99 | } catch (ReflectiveOperationException e) { 100 | e.printStackTrace(); 101 | } 102 | 103 | return yamlConfiguration.saveToString(); 104 | } 105 | 106 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/utils/versionutils/ClientVersion.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.utils.versionutils; 2 | 3 | /** 4 | * Client Version. 5 | * This is a nice tool for minecraft's protocol versions. 6 | * You won't have to memorize the protocol version, just memorize the client version 7 | * as the version you see in the minecraft launcher. 8 | * 9 | * @author Retrooper 10 | */ 11 | public enum ClientVersion { 12 | v_1_7_10(5), 13 | v_1_8(47), 14 | v_1_9(107), 15 | v_1_9_1(108), 16 | v_1_9_2(109), 17 | v_1_9_3(110), 18 | v_1_10(210), 19 | v_1_11(315), 20 | v_1_11_1(316), 21 | v_1_12(335), 22 | v_1_12_1(338), 23 | v_1_12_2(340), 24 | v_1_13(393), 25 | v_1_13_1(401), 26 | v_1_13_2(404), 27 | v_1_14(477), 28 | v_1_14_1(480), 29 | v_1_14_2(485), 30 | v_1_14_3(490), 31 | v_1_14_4(498), 32 | v_1_15(573), 33 | v_1_15_1(575), 34 | v_1_15_2(578), 35 | v_1_16(735), 36 | v_1_16_1(736), 37 | v_1_16_2(751), 38 | v_1_16_3(753), 39 | v_1_16_4(754), 40 | v_1_17(755), 41 | v_1_17_1(756), 42 | v_1_18(757), 43 | v1_18_2(758), 44 | UNKNOWN(Integer.MAX_VALUE); 45 | 46 | private final int protocolVersion; 47 | 48 | ClientVersion(int protocolVersion) { 49 | this.protocolVersion = protocolVersion; 50 | } 51 | 52 | /** 53 | * Get a ClientVersion enum by protocol version. 54 | * 55 | * @param protocolVersion The protocol version integer 56 | * @return ClientVersion 57 | */ 58 | public static ClientVersion getClientVersion(int protocolVersion) { 59 | 60 | for (ClientVersion version : values()) { 61 | 62 | if (protocolVersion == version.protocolVersion) return version; 63 | } 64 | 65 | return UNKNOWN; 66 | } 67 | 68 | /** 69 | * Protocol version of this client version. 70 | * 71 | * @return Protocol version. 72 | */ 73 | public int getProtocolVersion() { 74 | return protocolVersion; 75 | } 76 | 77 | /** 78 | * Is this client version newer than the compared client version? 79 | * This method simply checks if this client version's protocol version is greater than 80 | * the compared client version's protocol version. 81 | * 82 | * @param target Compared client version. 83 | * @return Is this client version newer than the compared client version. 84 | */ 85 | public boolean isHigherThan(ClientVersion target) { 86 | return protocolVersion > target.protocolVersion; 87 | } 88 | 89 | /** 90 | * Is this client version newer than or equal to the compared client version? 91 | * This method simply checks if this client version's protocol version is newer than or equal to 92 | * the compared client version's protocol version. 93 | * 94 | * @param target Compared client version. 95 | * @return Is this client version newer than or equal to the compared client version. 96 | */ 97 | public boolean isHigherThanOrEquals(ClientVersion target) { 98 | return protocolVersion >= target.protocolVersion; 99 | } 100 | 101 | /** 102 | * Is this client version older than the compared client version? 103 | * This method simply checks if this client version's protocol version is less than 104 | * the compared client version's protocol version. 105 | * 106 | * @param target Compared client version. 107 | * @return Is this client version older than the compared client version. 108 | */ 109 | public boolean isLowerThan(ClientVersion target) { 110 | return protocolVersion < target.protocolVersion; 111 | } 112 | 113 | /** 114 | * Is this client version older than or equal to the compared client version? 115 | * This method simply checks if this client version's protocol version is older than or equal to 116 | * the compared client version's protocol version. 117 | * 118 | * @param target Compared client version. 119 | * @return Is this client version older than or equal to the compared client version. 120 | */ 121 | public boolean isLowerThanOrEquals(ClientVersion target) { 122 | return protocolVersion <= target.protocolVersion; 123 | } 124 | 125 | /** 126 | * Is this client version equal to the compared client version. 127 | * This method simply checks if this client version's protocol version 128 | * is equal to the compared client version's protocol version. 129 | * 130 | * @param target Compared 131 | * @return Is this client version equal to the compared client version. 132 | */ 133 | public boolean equals(ClientVersion target) { 134 | return protocolVersion == target.protocolVersion; 135 | } 136 | 137 | @Override 138 | public String toString() { 139 | return this.name().substring(2).replace("_", "."); 140 | } 141 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/utils/MiscUtils.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.utils; 2 | 3 | import com.google.common.base.Charsets; 4 | import org.apache.commons.lang3.SystemUtils; 5 | import org.bukkit.Bukkit; 6 | import org.bukkit.Material; 7 | import org.bukkit.command.ConsoleCommandSender; 8 | import org.bukkit.configuration.InvalidConfigurationException; 9 | import org.bukkit.configuration.file.YamlConfiguration; 10 | import org.bukkit.inventory.ItemStack; 11 | 12 | import java.io.File; 13 | import java.io.FileInputStream; 14 | import java.io.IOException; 15 | import java.io.InputStreamReader; 16 | import java.util.Collection; 17 | import java.util.Iterator; 18 | import java.util.List; 19 | import java.util.Random; 20 | 21 | public final class MiscUtils { 22 | 23 | private MiscUtils() { 24 | } 25 | 26 | public static final ItemStack EMPTY_ITEM = new ItemStack(Material.AIR); 27 | 28 | public static YamlConfiguration loadConfigurationUTF_8(final File file) { 29 | 30 | final YamlConfiguration config = new YamlConfiguration(); 31 | 32 | try { 33 | 34 | final FileInputStream stream = new FileInputStream(file); 35 | 36 | config.load(new InputStreamReader(stream, Charsets.UTF_8)); 37 | 38 | } catch (IOException | InvalidConfigurationException e) { 39 | e.printStackTrace(); 40 | } 41 | 42 | return config; 43 | } 44 | 45 | /** 46 | * Initialize static fields inside the specified classes. 47 | * 48 | * @param paths The class path 49 | * @throws ClassNotFoundException if one of the classes is not present 50 | */ 51 | public static void initializeClasses(final String... paths) throws ClassNotFoundException { 52 | for (final String path : paths) Class.forName(path); 53 | } 54 | 55 | public static String wrapString(final String str, int wrapLength) { 56 | if (str == null) { 57 | 58 | return null; 59 | 60 | } else { 61 | 62 | if (wrapLength < 1) wrapLength = 1; 63 | 64 | int inputLineLength = str.length(); 65 | 66 | int offset = 0; 67 | 68 | StringBuilder wrappedLine = new StringBuilder(inputLineLength + 32); 69 | 70 | while (inputLineLength - offset > wrapLength) { 71 | 72 | if (str.charAt(offset) == ' ') { 73 | 74 | offset++; 75 | 76 | } else { 77 | 78 | int spaceToWrapAt = str.lastIndexOf(32, wrapLength + offset); 79 | 80 | if (spaceToWrapAt >= offset) { 81 | 82 | wrappedLine.append(str, offset, spaceToWrapAt); 83 | 84 | wrappedLine.append(SystemUtils.LINE_SEPARATOR); 85 | 86 | offset = spaceToWrapAt + 1; 87 | 88 | } else { 89 | 90 | spaceToWrapAt = str.indexOf(32, wrapLength + offset); 91 | 92 | if (spaceToWrapAt >= 0) { 93 | 94 | wrappedLine.append(str, offset, spaceToWrapAt); 95 | 96 | wrappedLine.append(SystemUtils.LINE_SEPARATOR); 97 | 98 | offset = spaceToWrapAt + 1; 99 | 100 | } else { 101 | 102 | wrappedLine.append(str.substring(offset)); 103 | 104 | offset = inputLineLength; 105 | } 106 | } 107 | } 108 | } 109 | 110 | wrappedLine.append(str.substring(offset)); 111 | 112 | return wrappedLine.toString(); 113 | } 114 | } 115 | 116 | public static String capitalizeFirstLetter(final String data) { 117 | 118 | final char firstLetter = Character.toTitleCase(data.substring(0, 1).charAt(0)); 119 | 120 | final String restLetters = data.substring(1).toLowerCase(); 121 | 122 | return firstLetter + restLetters; 123 | } 124 | 125 | @SuppressWarnings("unchecked") 126 | public static E randomElement(final Collection collection) { 127 | if (collection.size() == 0) return null; 128 | 129 | int index = new Random().nextInt(collection.size()); 130 | 131 | if (collection instanceof List) { 132 | 133 | return ((List) collection).get(index); 134 | 135 | } else { 136 | 137 | Iterator iter = collection.iterator(); 138 | 139 | for (int i = 0; i < index; i++) iter.next(); 140 | 141 | return iter.next(); 142 | } 143 | } 144 | 145 | public static void consoleCommand(final Collection commands) { 146 | if (commands == null || commands.size() == 0) return; 147 | 148 | ConsoleCommandSender console = Bukkit.getConsoleSender(); 149 | 150 | if (Bukkit.isPrimaryThread()) { 151 | 152 | commands.forEach(command -> Bukkit.dispatchCommand(console, command)); 153 | 154 | } else { 155 | 156 | TaskUtils.task(() -> commands.forEach(command -> Bukkit.dispatchCommand(console, command))); 157 | } 158 | } 159 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/checks/types/AbstractCheck.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.checks.types; 2 | 3 | import me.nik.anticheatbase.Anticheat; 4 | import me.nik.anticheatbase.api.events.AnticheatViolationEvent; 5 | import me.nik.anticheatbase.checks.annotations.Development; 6 | import me.nik.anticheatbase.checks.annotations.Experimental; 7 | import me.nik.anticheatbase.checks.enums.CheckCategory; 8 | import me.nik.anticheatbase.checks.enums.CheckType; 9 | import me.nik.anticheatbase.files.Config; 10 | import me.nik.anticheatbase.files.commentedfiles.CommentedFileConfiguration; 11 | import me.nik.anticheatbase.managers.profile.Profile; 12 | import me.nik.anticheatbase.utils.BetterStream; 13 | import me.nik.anticheatbase.utils.MiscUtils; 14 | import org.bukkit.Bukkit; 15 | import org.bukkit.entity.Player; 16 | 17 | import java.util.LinkedHashSet; 18 | import java.util.Set; 19 | 20 | public abstract class AbstractCheck { 21 | 22 | protected final Profile profile; 23 | 24 | private final boolean enabled; 25 | 26 | private final Set commands = new LinkedHashSet<>(); 27 | 28 | private final boolean enabledSetback; 29 | 30 | private final String checkName, checkType, fullCheckName, description; 31 | private final boolean experimental; 32 | private final CheckCategory checkCategory; 33 | private final boolean development; 34 | private int vl; 35 | private final int maxVl; 36 | private float buffer; 37 | private String verbose; //TODO: USE STRINGBUILDER 38 | 39 | public AbstractCheck(Profile profile, CheckType check, String type, String description) { 40 | 41 | this.profile = profile; 42 | this.checkName = check.getCheckName(); 43 | this.checkType = type; 44 | this.description = description; 45 | 46 | final CommentedFileConfiguration config = Anticheat.getInstance().getChecks(); 47 | final String checkName = this.checkName.toLowerCase(); 48 | final String checkType = type.toLowerCase().replace(" ", "_"); 49 | 50 | this.enabledSetback = !Config.Setting.GHOST_MODE.getBoolean() 51 | && (check == CheckType.SPEED || check == CheckType.FLY || check == CheckType.MOTION); 52 | 53 | this.enabled = type.isEmpty() 54 | ? config.getBoolean(checkName + ".enabled") 55 | : config.getBoolean(checkName + "." + checkType + ".enabled", config.getBoolean(checkName + "." + checkType)); 56 | 57 | this.maxVl = config.getInt(checkName + ".max_vl"); 58 | 59 | /* 60 | This is null inside GUI's 61 | */ 62 | if (profile != null) { 63 | this.commands.addAll( 64 | BetterStream.applyAndGet(config.getStringList(checkName + ".commands"), 65 | command -> command.replace("%player%", profile.getPlayer().getName()) 66 | ) 67 | ); 68 | 69 | } 70 | 71 | Class clazz = this.getClass(); 72 | 73 | this.experimental = clazz.isAnnotationPresent(Experimental.class); 74 | 75 | this.development = clazz.isAnnotationPresent(Development.class); 76 | 77 | this.checkCategory = check.getCheckCategory(); 78 | 79 | this.fullCheckName = this.checkName + (type.isEmpty() ? "" : (" (" + type + ")")); 80 | } 81 | 82 | public String getVerbose() { 83 | return verbose; 84 | } 85 | 86 | public String getFullCheckName() { 87 | return fullCheckName; 88 | } 89 | 90 | protected void debug(Object info) { 91 | Bukkit.broadcastMessage(String.valueOf(info)); 92 | } 93 | 94 | public void fail(String verbose) { 95 | 96 | this.verbose = verbose; 97 | 98 | fail(); 99 | } 100 | 101 | public void fail() { 102 | 103 | //Development 104 | if (this.development) return; 105 | 106 | //Just to make sure 107 | if (this.vl < 0) this.vl = 0; 108 | 109 | final Player p = profile.getPlayer(); 110 | 111 | if (p == null) return; 112 | 113 | AnticheatViolationEvent violationEvent = new AnticheatViolationEvent( 114 | p, 115 | this.checkName, 116 | this.description, 117 | this.checkType, 118 | verbose, 119 | //Increase the violations here 120 | this.vl++, 121 | this.maxVl, 122 | this.experimental); 123 | 124 | Bukkit.getPluginManager().callEvent(violationEvent); 125 | 126 | if (violationEvent.isCancelled() || this.experimental) { 127 | 128 | this.vl--; 129 | 130 | return; 131 | } 132 | 133 | if (this.enabledSetback) profile.getMovementData().getSetbackProcessor().setback(true); 134 | 135 | if (this.vl >= this.maxVl) { 136 | 137 | MiscUtils.consoleCommand(this.commands); 138 | 139 | this.vl = 0; 140 | this.buffer = 0; 141 | } 142 | } 143 | 144 | public CheckCategory getCategory() { 145 | return checkCategory; 146 | } 147 | 148 | public void resetVl() { 149 | this.vl = 0; 150 | } 151 | 152 | public int getVl() { 153 | return this.vl; 154 | } 155 | 156 | public void setVl(int vl) { 157 | this.vl = vl; 158 | } 159 | 160 | protected float increaseBuffer() { 161 | return this.buffer++; 162 | } 163 | 164 | protected float increaseBufferBy(double amount) { 165 | return this.buffer += amount; 166 | } 167 | 168 | protected float decreaseBuffer() { 169 | return this.buffer == 0 ? 0 : (this.buffer = Math.max(0, this.buffer - 1)); 170 | } 171 | 172 | protected float decreaseBufferBy(double amount) { 173 | return this.buffer == 0 ? 0 : (this.buffer = (float) Math.max(0, this.buffer - amount)); 174 | } 175 | 176 | public void resetBuffer() { 177 | this.buffer = 0; 178 | } 179 | 180 | protected float getBuffer() { 181 | return this.buffer; 182 | } 183 | 184 | public boolean isEnabled() { 185 | return this.enabled; 186 | } 187 | 188 | public String getCheckName() { 189 | return this.checkName; 190 | } 191 | 192 | public String getCheckType() { 193 | return this.checkType; 194 | } 195 | 196 | public String getDescription() { 197 | return this.description; 198 | } 199 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/Anticheat.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase; 2 | 3 | import com.comphenix.protocol.ProtocolLibrary; 4 | import me.nik.anticheatbase.commands.CommandManager; 5 | import me.nik.anticheatbase.files.Checks; 6 | import me.nik.anticheatbase.files.Config; 7 | import me.nik.anticheatbase.files.commentedfiles.CommentedFileConfiguration; 8 | import me.nik.anticheatbase.listeners.ClientBrandListener; 9 | import me.nik.anticheatbase.listeners.ProfileListener; 10 | import me.nik.anticheatbase.listeners.ViolationListener; 11 | import me.nik.anticheatbase.managers.AlertManager; 12 | import me.nik.anticheatbase.managers.logs.LogManager; 13 | import me.nik.anticheatbase.managers.profile.ProfileManager; 14 | import me.nik.anticheatbase.managers.themes.ThemeManager; 15 | import me.nik.anticheatbase.managers.threads.ThreadManager; 16 | import me.nik.anticheatbase.nms.NmsManager; 17 | import me.nik.anticheatbase.processors.listeners.BukkitListener; 18 | import me.nik.anticheatbase.processors.listeners.NetworkListener; 19 | import me.nik.anticheatbase.tasks.LogsTask; 20 | import me.nik.anticheatbase.tasks.TickTask; 21 | import me.nik.anticheatbase.tasks.ViolationTask; 22 | import me.nik.anticheatbase.utils.ChatUtils; 23 | import me.nik.anticheatbase.utils.MiscUtils; 24 | import me.nik.anticheatbase.utils.ReflectionUtils; 25 | import org.bukkit.Bukkit; 26 | import org.bukkit.event.HandlerList; 27 | import org.bukkit.plugin.java.JavaPlugin; 28 | 29 | import java.util.Arrays; 30 | 31 | /** 32 | * A simple and efficient anticheat base 33 | * 34 | * @author Nik 35 | */ 36 | public class Anticheat extends JavaPlugin { 37 | 38 | private static Anticheat instance; 39 | 40 | private Config configuration; 41 | private Checks checks; 42 | 43 | private ProfileManager profileManager; 44 | private final NmsManager nmsManager = new NmsManager(); 45 | private LogManager logManager; 46 | private ThreadManager threadManager; 47 | 48 | private AlertManager alertManager; 49 | private ThemeManager themeManager; 50 | 51 | @Override 52 | public void onEnable() { 53 | 54 | instance = this; 55 | 56 | //Initialize 57 | (this.configuration = new Config(this)).initialize(); 58 | (this.checks = new Checks(this)).initialize(); 59 | (this.profileManager = new ProfileManager()).initialize(); 60 | (this.themeManager = new ThemeManager(this)).initialize(); 61 | (this.logManager = new LogManager(this)).initialize(); 62 | (this.threadManager = new ThreadManager(this)).initialize(); 63 | (this.alertManager = new AlertManager()).initialize(); 64 | 65 | //Tasks 66 | new TickTask(this).runTaskTimerAsynchronously(this, 50L, 0L); 67 | 68 | if (Config.Setting.LOGS_ENABLED.getBoolean()) { 69 | new LogsTask(this).runTaskTimerAsynchronously(this, 6000L, 6000L); 70 | } 71 | 72 | new ViolationTask(this).runTaskTimerAsynchronously(this, 73 | Config.Setting.CHECK_SETTINGS_VIOLATION_RESET_INTERVAL.getLong() * 1200L, 74 | Config.Setting.CHECK_SETTINGS_VIOLATION_RESET_INTERVAL.getLong() * 1200L); 75 | 76 | //Packet Listeners 77 | Arrays.asList( 78 | new NetworkListener(this), 79 | new ClientBrandListener(this) 80 | ).forEach(packetListener -> ProtocolLibrary.getProtocolManager().addPacketListener(packetListener)); 81 | 82 | //Bukkit Listeners 83 | Arrays.asList( 84 | new ProfileListener(this), 85 | new ViolationListener(this), 86 | new BukkitListener() 87 | ).forEach(listener -> Bukkit.getPluginManager().registerEvents(listener, this)); 88 | 89 | //Load Commands 90 | getCommand("anticheat").setExecutor(new CommandManager(this)); 91 | 92 | //We're most likely going to be using transactions - ping pongs, So we need to do this for ViaVersion 93 | System.setProperty("com.viaversion.handlePingsAsInvAcknowledgements", "true"); 94 | 95 | //Initialize static variables to make sure our threads won't get affected when they run for the first time. 96 | try { 97 | 98 | MiscUtils.initializeClasses( 99 | "me.nik.anticheatbase.utils.fastmath.FastMath", 100 | "me.nik.anticheatbase.utils.fastmath.NumbersUtils", 101 | "me.nik.anticheatbase.utils.fastmath.FastMathLiteralArrays", 102 | "me.nik.anticheatbase.utils.minecraft.MathHelper", 103 | "me.nik.anticheatbase.utils.CollisionUtils", 104 | "me.nik.anticheatbase.utils.MoveUtils" 105 | ); 106 | 107 | } catch (ClassNotFoundException e) { 108 | 109 | //Impossible unless we made a mistake 110 | ChatUtils.log("An error was thrown during initialization, The anticheat may not work properly."); 111 | 112 | e.printStackTrace(); 113 | } 114 | } 115 | 116 | @Override 117 | public void onDisable() { 118 | 119 | //Shutdown all managers 120 | this.configuration.shutdown(); 121 | this.checks.shutdown(); 122 | this.profileManager.shutdown(); 123 | this.alertManager.shutdown(); 124 | this.threadManager.shutdown(); 125 | this.themeManager.shutdown(); 126 | 127 | //Clear reflection cache 128 | ReflectionUtils.clear(); 129 | 130 | //Unregister any listeners 131 | HandlerList.unregisterAll(this); 132 | ProtocolLibrary.getProtocolManager().removePacketListeners(this); 133 | 134 | //Cancel all tasks 135 | Bukkit.getScheduler().cancelTasks(this); 136 | 137 | instance = null; 138 | } 139 | 140 | public CommentedFileConfiguration getConfiguration() { 141 | return this.configuration.getConfig(); 142 | } 143 | 144 | public CommentedFileConfiguration getChecks() { 145 | return this.checks.getConfig(); 146 | } 147 | 148 | public ThemeManager getThemeManager() { 149 | return themeManager; 150 | } 151 | 152 | public ProfileManager getProfileManager() { 153 | return profileManager; 154 | } 155 | 156 | public LogManager getLogManager() { 157 | return logManager; 158 | } 159 | 160 | public ThreadManager getThreadManager() { 161 | return threadManager; 162 | } 163 | 164 | public AlertManager getAlertManager() { 165 | return alertManager; 166 | } 167 | 168 | public NmsManager getNmsManager() { 169 | return nmsManager; 170 | } 171 | 172 | public static Anticheat getInstance() { 173 | return instance; 174 | } 175 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/managers/profile/Profile.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.managers.profile; 2 | 3 | import me.nik.anticheatbase.Anticheat; 4 | import me.nik.anticheatbase.enums.Permissions; 5 | import me.nik.anticheatbase.files.Config; 6 | import me.nik.anticheatbase.managers.threads.ProfileThread; 7 | import me.nik.anticheatbase.playerdata.data.impl.ActionData; 8 | import me.nik.anticheatbase.playerdata.data.impl.CombatData; 9 | import me.nik.anticheatbase.playerdata.data.impl.ConnectionData; 10 | import me.nik.anticheatbase.playerdata.data.impl.MovementData; 11 | import me.nik.anticheatbase.playerdata.data.impl.RotationData; 12 | import me.nik.anticheatbase.playerdata.data.impl.TeleportData; 13 | import me.nik.anticheatbase.playerdata.data.impl.VehicleData; 14 | import me.nik.anticheatbase.playerdata.data.impl.VelocityData; 15 | import me.nik.anticheatbase.processors.Packet; 16 | import me.nik.anticheatbase.utils.ChatUtils; 17 | import me.nik.anticheatbase.utils.TaskUtils; 18 | import me.nik.anticheatbase.utils.custom.CheckHolder; 19 | import me.nik.anticheatbase.utils.custom.Exempt; 20 | import me.nik.anticheatbase.utils.versionutils.ClientVersion; 21 | import me.nik.anticheatbase.utils.versionutils.VersionUtils; 22 | import org.bukkit.entity.Player; 23 | 24 | import java.util.UUID; 25 | 26 | /** 27 | * A profile class containing every single information we need 28 | */ 29 | public class Profile { 30 | 31 | //------------------------------------------- 32 | private final ActionData actionData; 33 | private final CombatData combatData; 34 | private final ConnectionData connectionData; 35 | private final MovementData movementData; 36 | private final RotationData rotationData; 37 | private final TeleportData teleportData; 38 | private final VelocityData velocityData; 39 | private final VehicleData vehicleData; 40 | //------------------------------------------- 41 | 42 | //-------------------------------------- 43 | private final CheckHolder checkHolder; 44 | //-------------------------------------- 45 | 46 | //-------------------------------------- 47 | private final ClientVersion version; 48 | private String client = "Unknown"; 49 | private final boolean bypass; 50 | //-------------------------------------- 51 | 52 | //------------------------------------------ 53 | private final ProfileThread profileThread; 54 | private final Player player; 55 | private final UUID uuid; 56 | //------------------------------------------ 57 | 58 | //--------------------------- 59 | private final Exempt exempt; 60 | //--------------------------- 61 | 62 | public Profile(Player player) { 63 | 64 | //Player Object 65 | this.player = player; 66 | 67 | //UUID 68 | this.uuid = player.getUniqueId(); 69 | 70 | //Version 71 | this.version = VersionUtils.getClientVersion(player); 72 | 73 | //Bypass 74 | this.bypass = !Config.Setting.DISABLE_BYPASS_PERMISSION.getBoolean() && player.hasPermission(Permissions.BYPASS.getPermission()); 75 | 76 | //Data 77 | this.actionData = new ActionData(this); 78 | this.combatData = new CombatData(); 79 | this.connectionData = new ConnectionData(); 80 | this.movementData = new MovementData(this); 81 | this.rotationData = new RotationData(this); 82 | this.teleportData = new TeleportData(); 83 | this.velocityData = new VelocityData(); 84 | this.vehicleData = new VehicleData(); 85 | 86 | //Check Holder 87 | this.checkHolder = new CheckHolder(this); 88 | 89 | //Exempt 90 | this.exempt = new Exempt(this); 91 | 92 | //Thread 93 | this.profileThread = Anticheat.getInstance().getThreadManager().getAvailableProfileThread(); 94 | 95 | //Initialize Checks 96 | reloadChecks(); 97 | } 98 | 99 | public boolean isBypassing() { 100 | return bypass; 101 | } 102 | 103 | public void handle(Packet packet) { 104 | 105 | if (this.player == null) return; 106 | 107 | this.connectionData.process(packet); 108 | this.actionData.process(packet); 109 | this.combatData.process(packet); 110 | this.movementData.process(packet); 111 | this.rotationData.process(packet); 112 | this.teleportData.process(packet); 113 | this.velocityData.process(packet); 114 | this.vehicleData.process(packet); 115 | 116 | if (skip(packet.getTimeStamp())) return; 117 | 118 | this.exempt.handleExempts(packet.getTimeStamp()); 119 | 120 | this.checkHolder.runChecks(packet); 121 | } 122 | 123 | public void kick(String reason) { 124 | 125 | if (this.player == null) return; 126 | 127 | TaskUtils.task(() -> this.player.kickPlayer(ChatUtils.format(reason))); 128 | } 129 | 130 | public ClientVersion getVersion() { 131 | return version; 132 | } 133 | 134 | public String getClient() { 135 | return client; 136 | } 137 | 138 | public void setClient(String client) { 139 | this.client = client; 140 | } 141 | 142 | public Player getPlayer() { 143 | return this.player; 144 | } 145 | 146 | public UUID getUUID() { 147 | return this.uuid; 148 | } 149 | 150 | public void reloadChecks() { 151 | this.checkHolder.registerAll(); 152 | } 153 | 154 | private boolean skip(long currentTime) { 155 | 156 | //You can add more conditions here 157 | return this.bypass; 158 | } 159 | 160 | public void handleTick(long currentTime) { 161 | //Handle the tick here 162 | } 163 | 164 | public TeleportData getTeleportData() { 165 | return teleportData; 166 | } 167 | 168 | public ActionData getActionData() { 169 | return actionData; 170 | } 171 | 172 | public CombatData getCombatData() { 173 | return combatData; 174 | } 175 | 176 | public ConnectionData getConnectionData() { 177 | return connectionData; 178 | } 179 | 180 | public MovementData getMovementData() { 181 | return movementData; 182 | } 183 | 184 | public RotationData getRotationData() { 185 | return rotationData; 186 | } 187 | 188 | public VelocityData getVelocityData() { 189 | return velocityData; 190 | } 191 | 192 | public VehicleData getVehicleData() { 193 | return vehicleData; 194 | } 195 | 196 | public CheckHolder getCheckHolder() { 197 | return checkHolder; 198 | } 199 | 200 | public Exempt isExempt() { 201 | return exempt; 202 | } 203 | 204 | public ProfileThread getProfileThread() { 205 | return profileThread; 206 | } 207 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/managers/logs/impl/FileExporter.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.managers.logs.impl; 2 | 3 | import me.nik.anticheatbase.Anticheat; 4 | import me.nik.anticheatbase.managers.logs.LogExporter; 5 | import me.nik.anticheatbase.managers.logs.PlayerLog; 6 | import me.nik.anticheatbase.utils.ChatUtils; 7 | 8 | import java.io.BufferedReader; 9 | import java.io.BufferedWriter; 10 | import java.io.File; 11 | import java.io.FileReader; 12 | import java.io.FileWriter; 13 | import java.io.IOException; 14 | import java.io.PrintWriter; 15 | import java.text.DateFormat; 16 | import java.text.ParseException; 17 | import java.text.SimpleDateFormat; 18 | import java.util.ArrayList; 19 | import java.util.Collection; 20 | import java.util.Date; 21 | import java.util.List; 22 | import java.util.concurrent.CompletableFuture; 23 | import java.util.concurrent.ExecutionException; 24 | import java.util.concurrent.TimeUnit; 25 | import java.util.concurrent.TimeoutException; 26 | 27 | public class FileExporter extends LogExporter { 28 | 29 | public FileExporter(Anticheat plugin) { 30 | super(plugin); 31 | } 32 | 33 | @Override 34 | public void initialize() { 35 | 36 | CompletableFuture.runAsync(() -> { 37 | 38 | try { 39 | 40 | if (!plugin.getDataFolder().exists()) plugin.getDataFolder().mkdir(); 41 | 42 | File logsFile = new File(plugin.getDataFolder(), "data.yml"); 43 | 44 | logsFile.createNewFile(); 45 | 46 | //---CHECK FOR OLD LOGS---\\ 47 | 48 | File tempFile = new File(plugin.getDataFolder(), "data_temp.yml"); 49 | 50 | tempFile.createNewFile(); 51 | 52 | BufferedReader reader = new BufferedReader(new FileReader(logsFile)); 53 | BufferedWriter writer = new BufferedWriter(new FileWriter(tempFile)); 54 | 55 | DateFormat format = new SimpleDateFormat("dd.MM.yyyy HH:mm"); 56 | Date currentDate = new Date(System.currentTimeMillis()); 57 | 58 | String currentLine; 59 | 60 | while ((currentLine = reader.readLine()) != null) { 61 | 62 | String[] data = currentLine.split(","); 63 | 64 | if (Math.abs(format.parse(data[5]).getTime() - currentDate.getTime()) > DELETE_DAYS) continue; 65 | 66 | writer.write(currentLine + System.lineSeparator()); 67 | } 68 | 69 | writer.close(); 70 | reader.close(); 71 | 72 | logsFile.delete(); 73 | 74 | tempFile.renameTo(logsFile); 75 | 76 | } catch (ParseException | IOException e) { 77 | e.printStackTrace(); 78 | } 79 | }); 80 | } 81 | 82 | @Override 83 | public void shutdown() { 84 | } 85 | 86 | @Override 87 | public void logMultiple(Collection logs) { 88 | try { 89 | File saveTo = new File(plugin.getDataFolder(), "data.yml"); 90 | 91 | if (!saveTo.exists()) saveTo.createNewFile(); 92 | 93 | FileWriter fw = new FileWriter(saveTo, true); 94 | 95 | PrintWriter pw = new PrintWriter(fw); 96 | 97 | for (PlayerLog log : logs) pw.println(log.toString()); 98 | 99 | pw.flush(); 100 | 101 | pw.close(); 102 | 103 | } catch (IOException e) { 104 | e.printStackTrace(); 105 | } 106 | } 107 | 108 | @Override 109 | public void log(PlayerLog log) { 110 | try { 111 | 112 | File saveTo = new File(plugin.getDataFolder(), "data.yml"); 113 | 114 | saveTo.createNewFile(); 115 | 116 | FileWriter fw = new FileWriter(saveTo, true); 117 | 118 | PrintWriter pw = new PrintWriter(fw); 119 | 120 | pw.println(log.toString()); 121 | 122 | pw.flush(); 123 | 124 | pw.close(); 125 | 126 | } catch (IOException e) { 127 | e.printStackTrace(); 128 | } 129 | } 130 | 131 | @Override 132 | public List getLogs() { 133 | 134 | final File file = new File(plugin.getDataFolder(), "data.yml"); 135 | 136 | if (!file.exists()) return new ArrayList<>(); 137 | 138 | CompletableFuture> future = CompletableFuture.supplyAsync(() -> { 139 | 140 | List logs = new ArrayList<>(); 141 | 142 | try (BufferedReader br = new BufferedReader(new FileReader(file))) { 143 | 144 | for (String line; (line = br.readLine()) != null; ) { 145 | 146 | String[] data = line.split(","); 147 | 148 | logs.add(new PlayerLog(data[0], data[1], data[2], data[3], data[4], data[5])); 149 | } 150 | 151 | } catch (IOException e) { 152 | e.printStackTrace(); 153 | } 154 | 155 | return logs; 156 | }); 157 | 158 | try { 159 | return future.get(5, TimeUnit.SECONDS); 160 | } catch (InterruptedException | ExecutionException | TimeoutException e) { 161 | 162 | ChatUtils.log("Took more than 5 seconds to load the player logs!"); 163 | 164 | e.printStackTrace(); 165 | } 166 | 167 | return new ArrayList<>(); 168 | } 169 | 170 | @Override 171 | public List getLogsForPlayer(String player) { 172 | 173 | final File file = new File(plugin.getDataFolder(), "data.yml"); 174 | 175 | if (!file.exists()) return new ArrayList<>(); 176 | 177 | CompletableFuture> future = CompletableFuture.supplyAsync(() -> { 178 | 179 | List logs = new ArrayList<>(); 180 | 181 | try (BufferedReader br = new BufferedReader(new FileReader(file))) { 182 | 183 | for (String line; (line = br.readLine()) != null; ) { 184 | 185 | String[] data = line.split(","); 186 | 187 | if (!data[1].equalsIgnoreCase(player)) continue; 188 | 189 | logs.add(new PlayerLog(data[0], data[1], data[2], data[3], data[4], data[5])); 190 | } 191 | 192 | } catch (IOException e) { 193 | e.printStackTrace(); 194 | } 195 | 196 | return logs; 197 | }); 198 | 199 | try { 200 | 201 | return future.get(5, TimeUnit.SECONDS); 202 | 203 | } catch (InterruptedException | ExecutionException | TimeoutException e) { 204 | 205 | ChatUtils.log("Took more than 5 seconds to load the player logs!"); 206 | 207 | e.printStackTrace(); 208 | } 209 | 210 | return new ArrayList<>(); 211 | } 212 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/utils/custom/aim/RotationHeuristics.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.utils.custom.aim; 2 | 3 | import me.nik.anticheatbase.utils.fastmath.FastMath; 4 | 5 | import java.util.HashSet; 6 | import java.util.Set; 7 | 8 | /** 9 | * A simple heuristics tool that can prove useful when coding AimAssist related checks. 10 | * Example usage: 11 | *

12 | * private final RotationHeuristics heuristics = new RotationHeuristics(100, 1.25F, 7.5F); 13 | * 14 | * @Override public void handle(Packet packet) { 15 | * if (!packet.isRotation() || profile.isExempt().aim(15, 0)) return; 16 | *

17 | * RotationData data = profile.getRotationData(); 18 | *

19 | * this.heuristics.process(data.getDeltaYaw()); 20 | *

21 | * if (!this.heuristics.isFinished()) return; 22 | *

23 | * final RotationHeuristics.HeuristicsResult result = this.heuristics.getResult(); 24 | *

25 | * final float average = result.getAverage(); 26 | * final float min = result.getMin(); 27 | * final float max = result.getMax(); 28 | * final int lowCount = result.getLowCount(); 29 | * final int highCount = result.getHighCount(); 30 | * final int duplicates = result.getDuplicates(); 31 | * final int roundedCount = result.getRoundedCount(); 32 | * } 33 | */ 34 | public class RotationHeuristics { 35 | 36 | private final int maxSize; 37 | private final float lowThreshold, highThreshold; 38 | private final Set distinctRotations = new HashSet<>(); 39 | private int size; 40 | private float average, min, max; 41 | private int highCount, lowCount, roundedCount; 42 | 43 | /** 44 | * @param maxSize The maximum sample size. 45 | * @param lowThreshold The amount to be considered as "low". 46 | * @param highThreshold The amount to be considered as "high". 47 | */ 48 | public RotationHeuristics(int maxSize, float lowThreshold, float highThreshold) { 49 | this.size = this.maxSize = maxSize; 50 | this.lowThreshold = lowThreshold; 51 | this.highThreshold = highThreshold; 52 | reset(); 53 | } 54 | 55 | /** 56 | * Process the rotation given if processing is not finished yet. 57 | * 58 | * @param rotation The rotation made. 59 | */ 60 | public void process(float rotation) { 61 | 62 | /* 63 | Collection is finished, No more data can be processed. 64 | */ 65 | if (isFinished()) return; 66 | 67 | /* 68 | If this is the last element we're going to process, Get the correct average amount. 69 | Otherwise add the amount in order to use the average variable as our sum. 70 | */ 71 | this.average = (this.size - 1) == 0 ? (this.average + rotation) / this.maxSize : this.average + rotation; 72 | 73 | /* 74 | Set implementations do not allow duplicates, So we can simply add the value there 75 | And use it in the end to get the duplicate amounts. 76 | */ 77 | this.distinctRotations.add(rotation); 78 | 79 | /* 80 | Update the high rotation count if the current one meets the high amount condition. 81 | */ 82 | this.highCount = rotation > this.highThreshold ? this.highCount + 1 : this.highCount; 83 | 84 | /* 85 | Update the low rotation count if the current one meets the low amount condition. 86 | */ 87 | this.lowCount = rotation < this.lowThreshold ? this.lowCount + 1 : this.lowCount; 88 | 89 | /* 90 | Update the min rotation if the current rotation is lower than the current min amount. 91 | */ 92 | this.min = Math.min(rotation, this.min); 93 | 94 | /* 95 | Update the max rotation if the current rotation is higher than the current max amount. 96 | */ 97 | this.max = Math.max(rotation, this.max); 98 | 99 | /* 100 | Update the rounded count if the current rotation is big enough and rounded. 101 | */ 102 | this.roundedCount = rotation > 1F && rotation % 1.5 != 0F 103 | /* 104 | Make sure any type of rounding is applied. 105 | */ 106 | && (FastMath.round(rotation) == 0F || rotation % 1 == 0F) ? this.roundedCount + 1 : this.roundedCount; 107 | 108 | /* 109 | Decrement the size since processing for this value is finished. 110 | */ 111 | this.size--; 112 | } 113 | 114 | public void reset() { 115 | this.size = this.maxSize; 116 | this.average = this.highCount = this.lowCount = this.roundedCount = 0; 117 | this.min = Float.MAX_VALUE; 118 | this.max = Float.MIN_VALUE; 119 | this.distinctRotations.clear(); 120 | } 121 | 122 | private boolean isFinished() { 123 | return this.size == 0; 124 | } 125 | 126 | /** 127 | * Get the heuristics result if processing is finished, Null otherwise. 128 | */ 129 | public HeuristicsResult getResult() { 130 | 131 | /* 132 | Collection is finished, Return the result and reset. 133 | */ 134 | if (isFinished()) { 135 | 136 | return new HeuristicsResult( 137 | this.average, this.min, this.max, 138 | (this.maxSize - this.distinctRotations.size()), 139 | this.highCount, this.lowCount, this.roundedCount 140 | ); 141 | } 142 | 143 | /* 144 | Analysis result not finished yet. 145 | */ 146 | return null; 147 | } 148 | 149 | public static class HeuristicsResult { 150 | 151 | private final float average, min, max; 152 | private final int duplicates, highCount, lowCount, roundedCount; 153 | 154 | public HeuristicsResult(float average, float min, float max, int duplicates, int highCount, int lowCount, int roundedCount) { 155 | this.average = average; 156 | this.min = min; 157 | this.max = max; 158 | this.duplicates = duplicates; 159 | this.highCount = highCount; 160 | this.lowCount = lowCount; 161 | this.roundedCount = roundedCount; 162 | } 163 | 164 | public float getAverage() { 165 | return average; 166 | } 167 | 168 | public float getMin() { 169 | return min; 170 | } 171 | 172 | public float getMax() { 173 | return max; 174 | } 175 | 176 | public int getDuplicates() { 177 | return duplicates; 178 | } 179 | 180 | public int getHighCount() { 181 | return highCount; 182 | } 183 | 184 | public int getLowCount() { 185 | return lowCount; 186 | } 187 | 188 | public int getRoundedCount() { 189 | return roundedCount; 190 | } 191 | } 192 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/utils/JsonBuilder.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.utils; 2 | 3 | import com.comphenix.protocol.wrappers.EnumWrappers; 4 | import com.comphenix.protocol.wrappers.WrappedChatComponent; 5 | import me.nik.anticheatbase.wrappers.WrapperPlayServerChat; 6 | import org.bukkit.Bukkit; 7 | import org.bukkit.entity.Player; 8 | 9 | import java.util.Collection; 10 | import java.util.Optional; 11 | import java.util.UUID; 12 | 13 | public class JsonBuilder { 14 | 15 | // Main message text 16 | private static String text; 17 | // Basic JSON message, without any events 18 | private static String json = "{\"text\":\"" + text + "\"}"; 19 | // Value of the hover events 20 | private String hover; 21 | // Value of the click events 22 | private String click; 23 | /** 24 | * Hover event type 25 | * 26 | * @see HoverEventType 27 | */ 28 | private HoverEventType hoverAction; 29 | /** 30 | * Click event type 31 | * 32 | * @see ClickEventType 33 | */ 34 | private ClickEventType clickAction; 35 | 36 | /** 37 | * @param text Main message text 38 | */ 39 | public JsonBuilder(String text) { 40 | JsonBuilder.text = ChatUtils.format(text); 41 | } 42 | 43 | /** 44 | * @param type Type of the event (show text, show achievement, etc.) 45 | * @param value Value of the text inside it 46 | */ 47 | public JsonBuilder setHoverEvent(JsonBuilder.HoverEventType type, String value) { 48 | hover = ChatUtils.format(value); 49 | hoverAction = type; 50 | return this; 51 | } 52 | 53 | /** 54 | * @param type Type of the event (suggest command, open URL, etc.) 55 | * @param value Value of the event 56 | */ 57 | public JsonBuilder setClickEvent(JsonBuilder.ClickEventType type, String value) { 58 | click = value; 59 | clickAction = type; 60 | return this; 61 | } 62 | 63 | public JsonBuilder buildText() { 64 | 65 | if (!getClick().isPresent() && !getHover().isPresent()) json = "{\"text\":\"" + text + "\"}"; 66 | 67 | if (!getClick().isPresent() && getHover().isPresent()) { 68 | 69 | if (hoverAction == HoverEventType.SHOW_ACHIEVEMENT) { 70 | 71 | json = "{\"text\":\"" + text + "\",\"hoverEvent\":{\"action\":\"" + hoverAction.getActionName() + "\",\"value\":\"achievement." + hover + "\"}}"; 72 | 73 | } else if (hoverAction == HoverEventType.SHOW_STATISTIC) { 74 | 75 | json = "{\"text\":\"" + text + "\",\"hoverEvent\":{\"action\":\"" + hoverAction.getActionName() + "\",\"value\":\"stat." + hover + "\"}}"; 76 | 77 | } else { 78 | 79 | json = "{\"text\":\"" + text + "\",\"hoverEvent\":{\"action\":\"" + hoverAction.getActionName() + "\",\"value\":\"" + hover + "\"}}"; 80 | } 81 | } 82 | 83 | if (getClick().isPresent() && getHover().isPresent()) { 84 | 85 | json = "{\"text\":\"" + text + "\",\"clickEvent\":{\"action\":\"" + clickAction.getActionName() + "\",\"value\":\"" + click + "\"},\"hoverEvent\":{\"action\":\"" + hoverAction.getActionName() + "\",\"value\":\"" + hover + "\"}}"; 86 | } 87 | 88 | if (getClick().isPresent() && !getHover().isPresent()) { 89 | 90 | json = "{\"text\":\"" + text + "\",\"clickEvent\":{\"action\":\"" + clickAction.getActionName() + "\",\"value\":\"" + click + "\"}}"; 91 | } 92 | 93 | return this; 94 | } 95 | 96 | /** 97 | * @param player Send the player the modified text in the builder 98 | */ 99 | public void sendMessage(Player player) { 100 | 101 | WrapperPlayServerChat chat = new WrapperPlayServerChat(); 102 | 103 | chat.setChatType(EnumWrappers.ChatType.CHAT); 104 | 105 | chat.setMessage(WrappedChatComponent.fromJson(json)); 106 | 107 | chat.sendPacket(player); 108 | } 109 | 110 | /** 111 | * @param players Send the players the modified text in the builder 112 | */ 113 | public void sendMessage(Collection players) { 114 | 115 | WrapperPlayServerChat chat = new WrapperPlayServerChat(); 116 | 117 | chat.setChatType(EnumWrappers.ChatType.CHAT); 118 | 119 | chat.setMessage(WrappedChatComponent.fromJson(json)); 120 | 121 | for (UUID uuid : players) { 122 | 123 | Player p = Bukkit.getPlayer(uuid); 124 | 125 | if (p == null) continue; 126 | 127 | chat.sendPacket(p); 128 | } 129 | } 130 | 131 | /** 132 | * @return The original message text, without any formatting 133 | */ 134 | public String getUnformattedText() { 135 | return text; 136 | } 137 | 138 | /** 139 | * @return The JSON syntax, after all the modifying. 140 | */ 141 | public String getJson() { 142 | return json; 143 | } 144 | 145 | /** 146 | * @return Optional of the hover value (to simplify null checks) 147 | */ 148 | private Optional getHover() { 149 | return Optional.of(hover); 150 | } 151 | 152 | /** 153 | * @return Optional of the click value (to simplify null checks) 154 | */ 155 | private Optional getClick() { 156 | return Optional.of(click); 157 | } 158 | 159 | /** 160 | * Click events 161 | */ 162 | public enum ClickEventType { 163 | 164 | /** 165 | * Open a URL on click 166 | */ 167 | OPEN_URL("open_url"), 168 | 169 | /** 170 | * Force the player to run a command on click 171 | */ 172 | RUN_COMMAND("run_command"), 173 | 174 | /** 175 | * Add text into the player's chat field 176 | */ 177 | SUGGEST_TEXT("suggest_command"); 178 | 179 | // JSON name of the events 180 | private final String actionName; 181 | 182 | ClickEventType(String actionName) { 183 | this.actionName = actionName; 184 | } 185 | 186 | /** 187 | * @return JSON name of the event 188 | */ 189 | public String getActionName() { 190 | return actionName; 191 | } 192 | } 193 | 194 | public enum HoverEventType { 195 | 196 | /** 197 | * Show text on hover 198 | */ 199 | SHOW_TEXT("show_text"), 200 | 201 | /** 202 | * Show an item information on hover 203 | */ 204 | SHOW_ITEM("show_item"), 205 | 206 | /** 207 | * Show an achievement on hover 208 | */ 209 | SHOW_ACHIEVEMENT("show_achievement"), 210 | 211 | /** 212 | * Show a statistic on hover (the same action name is due to them being the same, however the prefix is different) 213 | * Since achievements take achievement.AchievementName, while statistics take stat.StatisticName 214 | */ 215 | SHOW_STATISTIC("show_achievement"); 216 | 217 | // JSON name of the event 218 | private final String actionName; 219 | 220 | HoverEventType(String actionName) { 221 | this.actionName = actionName; 222 | } 223 | 224 | /** 225 | * @return JSON name of the event 226 | */ 227 | public String getActionName() { 228 | return actionName; 229 | } 230 | } 231 | } -------------------------------------------------------------------------------- /src/main/java/me/nik/anticheatbase/playerdata/data/impl/RotationData.java: -------------------------------------------------------------------------------- 1 | package me.nik.anticheatbase.playerdata.data.impl; 2 | 3 | import me.nik.anticheatbase.managers.profile.Profile; 4 | import me.nik.anticheatbase.playerdata.data.Data; 5 | import me.nik.anticheatbase.playerdata.processors.impl.CinematicProcessor; 6 | import me.nik.anticheatbase.playerdata.processors.impl.SensitivityProcessor; 7 | import me.nik.anticheatbase.processors.Packet; 8 | import me.nik.anticheatbase.tasks.TickTask; 9 | import me.nik.anticheatbase.utils.ChatUtils; 10 | import me.nik.anticheatbase.utils.MathUtils; 11 | import me.nik.anticheatbase.utils.versionutils.ClientVersion; 12 | import me.nik.anticheatbase.wrappers.WrapperPlayClientLook; 13 | import me.nik.anticheatbase.wrappers.WrapperPlayClientPositionLook; 14 | 15 | public class RotationData implements Data { 16 | 17 | private final Profile profile; 18 | 19 | private final SensitivityProcessor sensitivityProcessor; 20 | private final CinematicProcessor cinematicProcessor; 21 | 22 | private float yaw, lastYaw, pitch, lastPitch, deltaYaw, lastDeltaYaw, 23 | deltaPitch, lastDeltaPitch, yawAccel, lastYawAccel, pitchAccel, lastPitchAccel; 24 | 25 | private int rotationsAfterTeleport, lastRotationTicks; 26 | 27 | public RotationData(Profile profile) { 28 | this.profile = profile; 29 | this.sensitivityProcessor = new SensitivityProcessor(profile); 30 | this.cinematicProcessor = new CinematicProcessor(profile); 31 | } 32 | 33 | private int invalidSnapThreshold; 34 | 35 | @Override 36 | public void process(Packet packet) { 37 | 38 | switch (packet.getType()) { 39 | 40 | case POSITION_LOOK: 41 | 42 | final WrapperPlayClientPositionLook posLookWrapper = packet.getPositionLookWrapper(); 43 | 44 | processRotation(posLookWrapper.getYaw(), posLookWrapper.getPitch()); 45 | 46 | break; 47 | 48 | case LOOK: 49 | 50 | final WrapperPlayClientLook lookWrapper = packet.getLookWrapper(); 51 | 52 | processRotation(lookWrapper.getYaw(), lookWrapper.getPitch()); 53 | 54 | break; 55 | 56 | case SERVER_POSITION: 57 | 58 | this.rotationsAfterTeleport = 0; 59 | 60 | break; 61 | } 62 | } 63 | 64 | public int getRotationsAfterTeleport() { 65 | return rotationsAfterTeleport; 66 | } 67 | 68 | private void processRotation(float yaw, float pitch) { 69 | 70 | //Duplicate rotation packet (1.17+) 71 | if (profile.getVersion().isHigherThanOrEquals(ClientVersion.v_1_17) 72 | && profile.getTeleportData().getTeleportTicks() > 1 73 | && yaw == this.yaw 74 | && pitch == this.pitch 75 | && profile.getActionData().getLastRidingTicks() > 1) return; 76 | 77 | final float lastYaw = this.yaw; 78 | 79 | this.lastYaw = lastYaw; 80 | this.yaw = yaw; 81 | 82 | final float lastPitch = this.pitch; 83 | 84 | this.lastPitch = lastPitch; 85 | this.pitch = pitch; 86 | 87 | final float lastDeltaYaw = this.deltaYaw; 88 | 89 | /* 90 | Clamp the deltaYaw to similarize the behavior between 1.8 -> latest versions of minecraft. 91 | (In 1.9 the packet's data is sent differently) 92 | */ 93 | final float deltaYaw = Math.abs(MathUtils.clamp180(Math.abs(yaw - lastYaw))); 94 | 95 | this.lastDeltaYaw = lastDeltaYaw; 96 | this.deltaYaw = deltaYaw; 97 | 98 | final float lastDeltaPitch = this.deltaPitch; 99 | final float deltaPitch = Math.abs(pitch - lastPitch); 100 | 101 | this.lastDeltaPitch = lastDeltaPitch; 102 | this.deltaPitch = deltaPitch; 103 | 104 | final float lastYawAccel = this.yawAccel; 105 | final float yawAccel = Math.abs(deltaYaw - lastDeltaYaw); 106 | 107 | this.lastYawAccel = lastYawAccel; 108 | this.yawAccel = yawAccel; 109 | 110 | final float lastPitchAccel = this.pitchAccel; 111 | final float pitchAccel = Math.abs(deltaPitch - lastDeltaPitch); 112 | 113 | this.lastPitchAccel = lastPitchAccel; 114 | this.pitchAccel = pitchAccel; 115 | 116 | //Process sensitivity 117 | this.sensitivityProcessor.process(); 118 | 119 | //Process cinematic 120 | this.cinematicProcessor.process(); 121 | 122 | this.rotationsAfterTeleport++; 123 | 124 | this.lastRotationTicks = TickTask.getCurrentTick(); 125 | 126 | /* 127 | This fixes the infamous bug which gets triggered when moving your client to the side in a small window 128 | And makes you snap around insanely fast, This will not affect legitimate players who snap around at any 129 | Sensitivity or DPI since it's almost impossible for your deltaYaw to be the same as the rotation constant 130 | While the acceleration is also zero for 10 times. 131 | 132 | This bug is very problematic since after it gets triggered all of your rotations will have 133 | An identical pattern, Which can lead to exploits, bugs or even falses. 134 | (Yes this is more problematic than you think, Due to the way sensitivity works in the minecraft client with the GUI) 135 | */ 136 | if (this.deltaYaw > 10F 137 | && this.deltaPitch == 0F 138 | && this.yawAccel == 0F 139 | && this.deltaYaw == this.sensitivityProcessor.getConstantYaw() 140 | && this.rotationsAfterTeleport > 5) { 141 | 142 | if (this.invalidSnapThreshold++ > 10) { 143 | 144 | ChatUtils.log("Kicking " + profile.getPlayer().getName() + " for triggering the snap bug."); 145 | 146 | profile.kick("Invalid Rotation Packet"); 147 | 148 | this.invalidSnapThreshold = 0; 149 | } 150 | 151 | } else this.invalidSnapThreshold = 0; 152 | } 153 | 154 | public SensitivityProcessor getSensitivityProcessor() { 155 | return sensitivityProcessor; 156 | } 157 | 158 | public CinematicProcessor getCinematicProcessor() { 159 | return cinematicProcessor; 160 | } 161 | 162 | public float getYaw() { 163 | return yaw; 164 | } 165 | 166 | public float getLastYaw() { 167 | return lastYaw; 168 | } 169 | 170 | public float getPitch() { 171 | return pitch; 172 | } 173 | 174 | public float getLastPitch() { 175 | return lastPitch; 176 | } 177 | 178 | public float getDeltaYaw() { 179 | return deltaYaw; 180 | } 181 | 182 | public float getLastDeltaYaw() { 183 | return lastDeltaYaw; 184 | } 185 | 186 | public float getDeltaPitch() { 187 | return deltaPitch; 188 | } 189 | 190 | public float getLastDeltaPitch() { 191 | return lastDeltaPitch; 192 | } 193 | 194 | public float getYawAccel() { 195 | return yawAccel; 196 | } 197 | 198 | public float getLastYawAccel() { 199 | return lastYawAccel; 200 | } 201 | 202 | public float getPitchAccel() { 203 | return pitchAccel; 204 | } 205 | 206 | public float getLastPitchAccel() { 207 | return lastPitchAccel; 208 | } 209 | 210 | public int getLastRotationTicks() { 211 | return MathUtils.elapsedTicks(this.lastRotationTicks); 212 | } 213 | } --------------------------------------------------------------------------------