├── 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 extends E> 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 extends E>) collection).get(index);
134 |
135 | } else {
136 |
137 | Iterator extends E> 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 extends AbstractCheck> 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 extends UUID> 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 | }
--------------------------------------------------------------------------------