├── .gitignore
├── API
├── pom.xml
└── src
│ └── main
│ └── java
│ └── ai
│ └── medusa
│ └── api
│ ├── check
│ ├── CheckInfo.java
│ └── MedusaCheck.java
│ └── listener
│ ├── MedusaFlagEvent.java
│ └── MedusaSendAlertEvent.java
├── ExampleAPI
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── ai
│ │ └── medusa
│ │ └── exampleapi
│ │ ├── ExamplePlugin.java
│ │ └── listener
│ │ └── ExampleListener.java
│ └── resources
│ └── plugin.yml
├── Impl
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── ai
│ │ └── medusa
│ │ └── anticheat
│ │ ├── Medusa.java
│ │ ├── MedusaPlugin.java
│ │ ├── check
│ │ ├── Check.java
│ │ └── impl
│ │ │ ├── combat
│ │ │ ├── aimassist
│ │ │ │ ├── AimAssistA.java
│ │ │ │ ├── AimAssistB.java
│ │ │ │ ├── AimAssistC.java
│ │ │ │ ├── AimAssistD.java
│ │ │ │ ├── AimAssistE.java
│ │ │ │ ├── AimAssistF.java
│ │ │ │ ├── AimAssistG.java
│ │ │ │ ├── AimAssistH.java
│ │ │ │ └── AimAssistI.java
│ │ │ ├── autoclicker
│ │ │ │ ├── AutoClickerA.java
│ │ │ │ ├── AutoClickerB.java
│ │ │ │ ├── AutoClickerC.java
│ │ │ │ └── AutoClickerD.java
│ │ │ ├── hitbox
│ │ │ │ └── HitBoxA.java
│ │ │ ├── killaura
│ │ │ │ ├── KillAuraA.java
│ │ │ │ ├── KillAuraB.java
│ │ │ │ ├── KillAuraC.java
│ │ │ │ ├── KillAuraD.java
│ │ │ │ ├── KillAuraE.java
│ │ │ │ ├── KillAuraF.java
│ │ │ │ └── KillAuraG.java
│ │ │ ├── reach
│ │ │ │ ├── ReachA.java
│ │ │ │ └── ReachB.java
│ │ │ └── velocity
│ │ │ │ └── VelocityA.java
│ │ │ ├── movement
│ │ │ ├── fastclimb
│ │ │ │ └── FastClimbA.java
│ │ │ ├── fly
│ │ │ │ ├── FlyA.java
│ │ │ │ ├── FlyB.java
│ │ │ │ └── FlyC.java
│ │ │ ├── jesus
│ │ │ │ ├── JesusA.java
│ │ │ │ └── JesusB.java
│ │ │ ├── motion
│ │ │ │ ├── MotionA.java
│ │ │ │ ├── MotionB.java
│ │ │ │ ├── MotionC.java
│ │ │ │ ├── MotionD.java
│ │ │ │ └── MotionE.java
│ │ │ ├── noslow
│ │ │ │ └── NoSlowA.java
│ │ │ ├── phase
│ │ │ │ └── PhaseA.java
│ │ │ └── speed
│ │ │ │ ├── SpeedA.java
│ │ │ │ ├── SpeedB.java
│ │ │ │ └── SpeedC.java
│ │ │ └── player
│ │ │ ├── hand
│ │ │ └── HandA.java
│ │ │ ├── inventory
│ │ │ ├── InventoryA.java
│ │ │ └── InventoryB.java
│ │ │ ├── protocol
│ │ │ ├── ProtocolA.java
│ │ │ ├── ProtocolB.java
│ │ │ ├── ProtocolC.java
│ │ │ ├── ProtocolD.java
│ │ │ ├── ProtocolE.java
│ │ │ ├── ProtocolF.java
│ │ │ ├── ProtocolG.java
│ │ │ ├── ProtocolH.java
│ │ │ ├── ProtocolI.java
│ │ │ └── ProtocolJ.java
│ │ │ ├── scaffold
│ │ │ ├── ScaffoldA.java
│ │ │ ├── ScaffoldB.java
│ │ │ ├── ScaffoldC.java
│ │ │ └── ScaffoldD.java
│ │ │ └── timer
│ │ │ ├── TimerA.java
│ │ │ ├── TimerB.java
│ │ │ └── TimerC.java
│ │ ├── command
│ │ ├── CommandInfo.java
│ │ ├── CommandManager.java
│ │ ├── MedusaCommand.java
│ │ └── impl
│ │ │ ├── Alerts.java
│ │ │ ├── Debug.java
│ │ │ ├── Info.java
│ │ │ ├── Theme.java
│ │ │ └── Violations.java
│ │ ├── config
│ │ ├── Config.java
│ │ └── ConfigValue.java
│ │ ├── data
│ │ ├── PlayerData.java
│ │ └── processor
│ │ │ ├── ActionProcessor.java
│ │ │ ├── ClickProcessor.java
│ │ │ ├── CombatProcessor.java
│ │ │ ├── PositionProcessor.java
│ │ │ ├── RotationProcessor.java
│ │ │ └── VelocityProcessor.java
│ │ ├── exempt
│ │ ├── ExemptProcessor.java
│ │ └── type
│ │ │ └── ExemptType.java
│ │ ├── listener
│ │ ├── BukkitEventListener.java
│ │ ├── ClientBrandListener.java
│ │ ├── JoinQuitListener.java
│ │ └── NetworkListener.java
│ │ ├── manager
│ │ ├── CheckManager.java
│ │ ├── PlayerDataManager.java
│ │ ├── ThemeManager.java
│ │ └── TickManager.java
│ │ ├── packet
│ │ ├── Packet.java
│ │ └── processor
│ │ │ ├── ReceivingPacketProcessor.java
│ │ │ └── SendingPacketProcessor.java
│ │ └── util
│ │ ├── ColorUtil.java
│ │ ├── HitboxExpansion.java
│ │ ├── MathUtil.java
│ │ ├── PlayerUtil.java
│ │ ├── ReflectionUtil.java
│ │ ├── ServerUtil.java
│ │ ├── anticheat
│ │ ├── AlertUtil.java
│ │ └── PunishUtil.java
│ │ └── type
│ │ ├── BoundingBox.java
│ │ ├── CustomLocation.java
│ │ ├── EvictingList.java
│ │ ├── Pair.java
│ │ └── RayTrace.java
│ └── resources
│ ├── config.yml
│ └── plugin.yml
├── LICENSE
├── README.md
└── pom.xml
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | *.iml
3 |
4 | *.jar
5 | *.class
6 |
7 | */target
--------------------------------------------------------------------------------
/API/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | Medusa
7 | ai.medusa
8 | 1.0
9 |
10 | 4.0.0
11 |
12 | API
13 | jar
14 | 1.1.6
15 |
16 |
17 |
18 |
19 | org.apache.maven.plugins
20 | maven-compiler-plugin
21 | 3.7.0
22 |
23 | 8
24 | 8
25 | -XDignore.symbol.file
26 |
27 |
28 |
29 |
30 |
31 | src/main/resources
32 | true
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/API/src/main/java/ai/medusa/api/check/CheckInfo.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.api.check;
2 |
3 | import java.lang.annotation.Retention;
4 | import java.lang.annotation.RetentionPolicy;
5 |
6 | @Retention(RetentionPolicy.RUNTIME)
7 | public @interface CheckInfo {
8 | String name();
9 | String description();
10 | boolean experimental() default false;
11 | }
12 |
--------------------------------------------------------------------------------
/API/src/main/java/ai/medusa/api/check/MedusaCheck.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.api.check;
2 |
3 | public interface MedusaCheck {
4 |
5 | CheckInfo getCheckInfo();
6 |
7 | String getPunishCommand();
8 |
9 | boolean isEnabled();
10 |
11 | int getMaxVl();
12 |
13 | int getVl();
14 |
15 | long getLastFlagTime();
16 | }
--------------------------------------------------------------------------------
/API/src/main/java/ai/medusa/api/listener/MedusaFlagEvent.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.api.listener;
2 |
3 | import ai.medusa.api.check.MedusaCheck;
4 | import lombok.Getter;
5 | import lombok.Setter;
6 | import org.bukkit.entity.Player;
7 | import org.bukkit.event.Cancellable;
8 | import org.bukkit.event.Event;
9 | import org.bukkit.event.HandlerList;
10 |
11 | public final class MedusaFlagEvent extends Event implements Cancellable {
12 |
13 | private boolean cancelled;
14 |
15 | private final Player player;
16 | private final MedusaCheck check;
17 |
18 |
19 | public MedusaFlagEvent(Player player, MedusaCheck check) {
20 | super(true);
21 | this.player = player;
22 | this.check = check;
23 | this.cancelled = false;
24 | }
25 |
26 | public Player getPlayer() {
27 | return player;
28 | }
29 |
30 | public MedusaCheck getCheck() {
31 | return check;
32 | }
33 |
34 | private static final HandlerList handlers = new HandlerList();
35 |
36 | public HandlerList getHandlers() {
37 | return handlers;
38 | }
39 |
40 | public static HandlerList getHandlerList() {
41 | return handlers;
42 | }
43 |
44 | @Override
45 | public boolean isCancelled() {
46 | return cancelled;
47 | }
48 |
49 | @Override
50 | public void setCancelled(final boolean b) {
51 | cancelled = b;
52 | }
53 | }
--------------------------------------------------------------------------------
/API/src/main/java/ai/medusa/api/listener/MedusaSendAlertEvent.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.api.listener;
2 |
3 | import ai.medusa.api.check.MedusaCheck;
4 | import lombok.Getter;
5 | import lombok.Setter;
6 | import net.md_5.bungee.api.chat.TextComponent;
7 | import org.bukkit.entity.Player;
8 | import org.bukkit.event.Cancellable;
9 | import org.bukkit.event.Event;
10 | import org.bukkit.event.HandlerList;
11 |
12 | public class MedusaSendAlertEvent extends Event implements Cancellable {
13 | private boolean cancelled;
14 | private final Player player;
15 | private final TextComponent message;
16 | private final MedusaCheck check;
17 | private final String info;
18 |
19 | public MedusaSendAlertEvent(final TextComponent message, final Player player, final MedusaCheck check, final String info) {
20 | super(true);
21 | this.player = player;
22 | this.message = message;
23 | this.check = check;
24 | this.info = info;
25 | this.cancelled = false;
26 | }
27 |
28 | private static final HandlerList handlers = new HandlerList();
29 |
30 | public HandlerList getHandlers() {
31 | return handlers;
32 | }
33 |
34 | public static HandlerList getHandlerList() {
35 | return handlers;
36 | }
37 |
38 | public Player getPlayer() {
39 | return player;
40 | }
41 |
42 | public TextComponent getMessage() {
43 | return message;
44 | }
45 |
46 | public MedusaCheck getCheck() {
47 | return check;
48 | }
49 |
50 | public String getInfo() {
51 | return info;
52 | }
53 |
54 | @Override
55 | public boolean isCancelled() {
56 | return cancelled;
57 | }
58 |
59 | @Override
60 | public void setCancelled(final boolean b) {
61 | cancelled = b;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/ExampleAPI/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | Medusa
7 | ai.medusa
8 | 1.0
9 |
10 | 4.0.0
11 |
12 | ExampleAPI
13 |
14 |
15 |
16 |
17 | org.apache.maven.plugins
18 | maven-compiler-plugin
19 | 3.7.0
20 |
21 | 8
22 | 8
23 | -XDignore.symbol.file
24 |
25 |
26 |
27 | org.apache.maven.plugins
28 | maven-shade-plugin
29 | 3.1.0
30 |
31 |
32 | package
33 |
34 | shade
35 |
36 |
37 | false
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | src/main/resources
46 | true
47 |
48 |
49 |
50 |
51 |
52 |
53 | ai.medusa
54 | API
55 | 1.1.6
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/ExampleAPI/src/main/java/ai/medusa/exampleapi/ExamplePlugin.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.exampleapi;
2 |
3 | import ai.medusa.exampleapi.listener.ExampleListener;
4 | import org.bukkit.Bukkit;
5 | import org.bukkit.plugin.java.JavaPlugin;
6 |
7 | public final class ExamplePlugin extends JavaPlugin {
8 |
9 | @Override
10 | public void onEnable() {
11 | Bukkit.getServer().getPluginManager().registerEvents(new ExampleListener(), this);
12 | Bukkit.broadcastMessage("Started ExamplePlugin using MedusaAPI");
13 | }
14 |
15 | @Override
16 | public void onDisable() {
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/ExampleAPI/src/main/java/ai/medusa/exampleapi/listener/ExampleListener.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.exampleapi.listener;
2 |
3 | import ai.medusa.api.check.MedusaCheck;
4 | import ai.medusa.api.listener.MedusaFlagEvent;
5 | import org.bukkit.Bukkit;
6 | import org.bukkit.entity.Player;
7 | import org.bukkit.event.EventHandler;
8 | import org.bukkit.event.Listener;
9 |
10 | public final class ExampleListener implements Listener {
11 | @EventHandler
12 | public void onMedusaFlag(final MedusaFlagEvent event) {
13 | final Player player = event.getPlayer();
14 | final MedusaCheck check = event.getCheck();
15 | final boolean cancelled = event.isCancelled();
16 |
17 | Bukkit.broadcastMessage(player.getName() + " flagged " + check.getCheckInfo().name());
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/ExampleAPI/src/main/resources/plugin.yml:
--------------------------------------------------------------------------------
1 | name: ExampleAPI
2 | version: 1.0
3 | main: ai.medusa.exampleapi.ExamplePlugin
4 | authors: [infinitesm]
5 | description: API usage example of Medusa Anticheat API
6 |
--------------------------------------------------------------------------------
/Impl/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | Medusa
7 | ai.medusa
8 | 1.0
9 |
10 | 4.0.0
11 |
12 | Impl
13 |
14 |
15 |
16 |
17 | org.apache.maven.plugins
18 | maven-compiler-plugin
19 | 3.7.0
20 |
21 | 8
22 | 8
23 | -XDignore.symbol.file
24 |
25 |
26 |
27 | org.apache.maven.plugins
28 | maven-shade-plugin
29 | 3.1.0
30 |
31 |
32 | package
33 |
34 | shade
35 |
36 |
37 | false
38 | Medusa
39 |
40 |
41 | io.github.retrooper.packetevents
42 | ai.medusa.anticheat.packetevents
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | src/main/resources
53 | true
54 |
55 |
56 |
57 |
58 |
59 |
60 | ai.medusa
61 | API
62 | 1.1.6
63 |
64 |
65 | com.github.retrooper
66 | packetevents
67 | 1.8-pre-9
68 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/Medusa.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat;
2 |
3 | import ai.medusa.anticheat.listener.BukkitEventListener;
4 | import ai.medusa.anticheat.listener.ClientBrandListener;
5 | import ai.medusa.anticheat.listener.NetworkListener;
6 | import ai.medusa.anticheat.listener.JoinQuitListener;
7 | import ai.medusa.anticheat.manager.CheckManager;
8 | import ai.medusa.anticheat.manager.PlayerDataManager;
9 | import ai.medusa.anticheat.manager.ThemeManager;
10 | import ai.medusa.anticheat.manager.TickManager;
11 | import ai.medusa.anticheat.manager.*;
12 | import io.github.retrooper.packetevents.PacketEvents;
13 | import lombok.Getter;
14 | import ai.medusa.anticheat.command.CommandManager;
15 | import ai.medusa.anticheat.config.Config;
16 | import ai.medusa.anticheat.packet.processor.ReceivingPacketProcessor;
17 | import ai.medusa.anticheat.packet.processor.SendingPacketProcessor;
18 | import org.bukkit.Bukkit;
19 | import org.bukkit.plugin.messaging.Messenger;
20 | import java.util.concurrent.ExecutorService;
21 | import java.util.concurrent.Executors;
22 |
23 | @Getter
24 | public enum Medusa {
25 |
26 | INSTANCE;
27 |
28 | private MedusaPlugin plugin;
29 |
30 | private long startTime;
31 |
32 | private final TickManager tickManager = new TickManager();
33 | private final ReceivingPacketProcessor receivingPacketProcessor = new ReceivingPacketProcessor();
34 | private final SendingPacketProcessor sendingPacketProcessor = new SendingPacketProcessor();
35 | private final PlayerDataManager playerDataManager = new PlayerDataManager();
36 | private final CommandManager commandManager = new CommandManager(this.getPlugin());
37 | private final ExecutorService packetExecutor = Executors.newSingleThreadExecutor();
38 |
39 | public void start(final MedusaPlugin plugin) {
40 | this.plugin = plugin;
41 | assert plugin != null : "Error while starting Medusa.";
42 |
43 | this.getPlugin().saveDefaultConfig();
44 | Config.updateConfig();
45 |
46 | CheckManager.setup();
47 | ThemeManager.setup();
48 |
49 | Bukkit.getOnlinePlayers().forEach(player -> this.getPlayerDataManager().add(player));
50 |
51 | getPlugin().saveDefaultConfig();
52 | getPlugin().getCommand("medusa").setExecutor(commandManager);
53 |
54 | tickManager.start();
55 |
56 | final Messenger messenger = Bukkit.getMessenger();
57 | messenger.registerIncomingPluginChannel(plugin, "MC|Brand", new ClientBrandListener());
58 |
59 | startTime = System.currentTimeMillis();
60 |
61 | Bukkit.getServer().getPluginManager().registerEvents(new BukkitEventListener(), plugin);
62 | Bukkit.getServer().getPluginManager().registerEvents(new ClientBrandListener(), plugin);
63 | Bukkit.getServer().getPluginManager().registerEvents(new JoinQuitListener(), plugin);
64 |
65 | PacketEvents.get().registerListener(new NetworkListener());
66 | }
67 |
68 | public void stop(final MedusaPlugin plugin) {
69 | this.plugin = plugin;
70 | assert plugin != null : "Error while shutting down Medusa.";
71 |
72 | tickManager.stop();
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/MedusaPlugin.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat;
2 |
3 | import io.github.retrooper.packetevents.PacketEvents;
4 | import io.github.retrooper.packetevents.utils.server.ServerVersion;
5 | import org.bukkit.plugin.java.JavaPlugin;
6 |
7 | public final class MedusaPlugin extends JavaPlugin {
8 |
9 | @Override
10 | public void onLoad() {
11 | PacketEvents.create(this).getSettings()
12 | .checkForUpdates(true)
13 | .backupServerVersion(ServerVersion.v_1_8_8);
14 |
15 | PacketEvents.get().load();
16 | }
17 |
18 | @Override
19 | public void onEnable() {
20 | PacketEvents.get().init();
21 | Medusa.INSTANCE.start(this);
22 | }
23 |
24 | @Override
25 | public void onDisable() {
26 | PacketEvents.get().terminate();
27 | Medusa.INSTANCE.stop(this);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/Check.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check;
2 |
3 | import ai.medusa.api.check.CheckInfo;
4 | import ai.medusa.api.check.MedusaCheck;
5 | import ai.medusa.api.listener.MedusaFlagEvent;
6 | import ai.medusa.anticheat.config.Config;
7 | import ai.medusa.anticheat.util.anticheat.PunishUtil;
8 | import ai.medusa.anticheat.data.PlayerData;
9 | import ai.medusa.anticheat.exempt.type.ExemptType;
10 | import ai.medusa.anticheat.util.anticheat.AlertUtil;
11 | import ai.medusa.anticheat.packet.Packet;
12 |
13 | import lombok.AccessLevel;
14 | import lombok.Getter;
15 | import lombok.Setter;
16 |
17 | import org.bukkit.Bukkit;
18 | import org.bukkit.ChatColor;
19 | import org.bukkit.Material;
20 |
21 | import java.util.Objects;
22 |
23 | @Getter
24 | @Setter
25 | public abstract class Check implements MedusaCheck {
26 |
27 | //Data for check.
28 | public final PlayerData data;
29 |
30 | //Check data from config.
31 | private final boolean enabled;
32 | private final int maxVl;
33 | private final String punishCommand;
34 |
35 | //Check information.
36 | private int vl;
37 | private long lastFlagTime;
38 | private CheckType checkType;
39 | private boolean debugging;
40 | @Getter(AccessLevel.NONE) @Setter(AccessLevel.NONE)
41 | public double buffer;
42 |
43 | //Because I'm lazy to change the annotation again.
44 | public String justTheName;
45 | public char type;
46 |
47 | public Check(final PlayerData data) {
48 | this.data = data;
49 |
50 | this.enabled = Config.ENABLED_CHECKS.contains(this.getClass().getSimpleName());
51 | this.maxVl = Config.MAX_VIOLATIONS.get(this.getClass().getSimpleName());
52 | this.punishCommand = Config.PUNISH_COMMANDS.get(this.getClass().getSimpleName());
53 |
54 | this.checkType = CheckType.fromPackageName(this.getClass().getPackage().getName());
55 |
56 | this.justTheName = this.getCheckInfo().name().split("\\(")[0].replace(" ", "");
57 | this.type = this.getCheckInfo().name().split("\\(")[1].split("\\)")[0].replaceAll(" ", "").toCharArray()[0];
58 | }
59 |
60 | public abstract void handle(final Packet packet);
61 |
62 | public void fail(final Object info) {
63 | final MedusaFlagEvent event = new MedusaFlagEvent(data.getPlayer(), this);
64 | Bukkit.getPluginManager().callEvent(event);
65 | if (event.isCancelled()) return;
66 |
67 | vl++;
68 |
69 | switch (this.getCheckType()) {
70 | case COMBAT:
71 | data.setCombatViolations(data.getCombatViolations() + 1);
72 | break;
73 | case MOVEMENT:
74 | data.setMovementViolations(data.getMovementViolations() + 1);
75 | break;
76 | case PLAYER:
77 | data.setPlayerViolations(data.getPlayerViolations() + 1);
78 | break;
79 | }
80 |
81 | data.setTotalViolations(data.getTotalViolations() + 1);
82 |
83 | if (System.currentTimeMillis() - lastFlagTime > Config.ALERT_COOLDOWN && vl >= Config.MIN_VL_TO_ALERT) {
84 | AlertUtil.handleAlert(this, data, Objects.toString(info));
85 | this.lastFlagTime = System.currentTimeMillis();
86 | }
87 |
88 | if (vl > maxVl) {
89 | PunishUtil.punish(this, data);
90 | }
91 | }
92 |
93 | public void fail() {
94 | fail("No info");
95 | }
96 |
97 | protected boolean isExempt(final ExemptType exemptType) {
98 | return data.getExemptProcessor().isExempt(exemptType);
99 | }
100 |
101 | protected boolean isExempt(final ExemptType...exemptTypes) {
102 | return data.getExemptProcessor().isExempt(exemptTypes);
103 | }
104 |
105 | protected long now() {
106 | return System.currentTimeMillis();
107 | }
108 |
109 | public CheckInfo getCheckInfo() {
110 | if (this.getClass().isAnnotationPresent(CheckInfo.class)) {
111 | return this.getClass().getAnnotation(CheckInfo.class);
112 | } else {
113 | System.err.println("CheckInfo annotation hasn't been added to the class " + this.getClass().getSimpleName() + ".");
114 | }
115 | return null;
116 | }
117 |
118 | public void debug(final Object... objects) {
119 | if (isDebugging()) {
120 | for (final Object object : objects) {
121 | data.getPlayer().sendMessage(ChatColor.GREEN + "[Medusa-Debug] " + ChatColor.GRAY + object);
122 | }
123 | }
124 | }
125 |
126 | public void debugToConsole(final Object... objects) {
127 | if (isDebugging()) {
128 | for (final Object object : objects) {
129 | System.out.println(ChatColor.GREEN + "[Medusa-Debug] " + ChatColor.GRAY + object);
130 | }
131 | }
132 | }
133 |
134 | public enum CheckType {
135 | COMBAT("Combat"),
136 | MOVEMENT("Movement"),
137 | PLAYER("Player");
138 |
139 | private final String name;
140 |
141 | CheckType(String name) {
142 | this.name = name;
143 | }
144 |
145 | public String getName() {
146 | return this.name;
147 | }
148 |
149 | public static CheckType fromPackageName(String packageName) {
150 | for (CheckType checkType : CheckType.values()) {
151 | if (packageName.contains(checkType.getName().toLowerCase())) {
152 | return checkType;
153 | }
154 | }
155 | return null;
156 | }
157 | }
158 |
159 | public boolean isBridging() {
160 | return data.getPlayer().getLocation().clone().subtract(0, 2, 0).getBlock().getType() == Material.AIR;
161 | }
162 | }
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/combat/aimassist/AimAssistA.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.combat.aimassist;
2 |
3 | import ai.medusa.api.check.CheckInfo;
4 | import ai.medusa.anticheat.check.Check;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.exempt.type.ExemptType;
7 | import ai.medusa.anticheat.packet.Packet;
8 |
9 | import java.util.function.Predicate;
10 |
11 | /**
12 | * Created on 10/24/2020 Package ai.medusa.anticheat.check.impl.combat.aim by infinitesm
13 | */
14 |
15 | @CheckInfo(name = "AimAssist (A)", description = "Checks for irregular movements in the rotation.")
16 | public final class AimAssistA extends Check {
17 |
18 | private final Predicate validRotation = rotation -> rotation > 3F && rotation < 35F;
19 |
20 | public AimAssistA(final PlayerData data) {
21 | super(data);
22 | }
23 |
24 | @Override
25 | public void handle(final Packet packet) {
26 | if (packet.isRotation()) {
27 | final float deltaPitch = Math.abs(data.getRotationProcessor().getDeltaPitch());
28 | final float deltaYaw = Math.abs(data.getRotationProcessor().getDeltaYaw() % 360F);
29 |
30 | final float pitch = Math.abs(data.getRotationProcessor().getPitch());
31 |
32 | final boolean invalidPitch = deltaPitch < 0.007 && validRotation.test(deltaYaw);
33 | final boolean invalidYaw = deltaYaw < 0.007 && validRotation.test(deltaPitch);
34 |
35 | final boolean exempt = isExempt(ExemptType.INSIDE_VEHICLE);
36 | final boolean invalid = !exempt && (invalidPitch || invalidYaw) && pitch < 89F;
37 |
38 | debug(String.format("deltaYaw=%.2f, deltaPitch=%.2f", deltaYaw, deltaPitch));
39 |
40 | if (invalid) {
41 | if (++buffer > 20) {
42 | fail(String.format("deltaYaw=%.2f, deltaPitch=%.2f", deltaYaw, deltaPitch));
43 | }
44 | } else {
45 | buffer = 0;
46 | }
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/combat/aimassist/AimAssistB.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.combat.aimassist;
2 |
3 | import ai.medusa.anticheat.check.Check;
4 | import ai.medusa.api.check.CheckInfo;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.exempt.type.ExemptType;
7 | import ai.medusa.anticheat.packet.Packet;
8 |
9 | /**
10 | * Created on 11/14/2020 Package ai.medusa.anticheat.check.impl.combat.aim by infinitesm
11 | */
12 |
13 | @CheckInfo(name = "AimAssist (B)", description = "Checks for rounded rotation.")
14 | public final class AimAssistB extends Check {
15 |
16 | public AimAssistB(final PlayerData data) {
17 | super(data);
18 | }
19 |
20 | @Override
21 | public void handle(final Packet packet) {
22 | if (packet.isRotation()) {
23 | final float deltaYaw = data.getRotationProcessor().getDeltaYaw() % 360F;
24 | final float deltaPitch = data.getRotationProcessor().getDeltaPitch();
25 |
26 | final boolean exempt = isExempt(ExemptType.TELEPORT);
27 |
28 | final boolean invalid = !exempt
29 | && (deltaPitch % 1 == 0 || deltaYaw % 1 == 0) && deltaPitch != 0 && deltaYaw != 0;
30 |
31 | debug(String.format("teleport=%b, buffer=%.2f", exempt, buffer));
32 |
33 | if (invalid) {
34 | if (++buffer > 4) {
35 | fail(String.format("buffer=%.2f", buffer));
36 | }
37 | } else {
38 | buffer = Math.max(0, buffer - 0.25);
39 | }
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/combat/aimassist/AimAssistC.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.combat.aimassist;
2 |
3 | import ai.medusa.anticheat.check.Check;
4 | import ai.medusa.api.check.CheckInfo;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.exempt.type.ExemptType;
7 | import ai.medusa.anticheat.packet.Packet;
8 |
9 | /**
10 | * Created 10/24/2020 Package ai.medusa.anticheat.check.impl.combat.aim by infinitesm
11 | */
12 |
13 | @CheckInfo(name = "AimAssist (C)", description = "Checks for constant rotation.", experimental = true)
14 | public final class AimAssistC extends Check {
15 |
16 | public AimAssistC(final PlayerData data) {
17 | super(data);
18 | }
19 |
20 | @Override
21 | public void handle(final Packet packet) {
22 | if (packet.isRotation()) {
23 | final float deltaYaw = data.getRotationProcessor().getDeltaYaw();
24 | final float deltaPitch = data.getRotationProcessor().getDeltaPitch();
25 |
26 | final double yawAccel = data.getRotationProcessor().getJoltYaw();
27 | final double pitchAccel = data.getRotationProcessor().getJoltPitch();
28 |
29 | final boolean exempt = isExempt(ExemptType.TELEPORT, ExemptType.CINEMATIC);
30 | final boolean invalidYaw = yawAccel < 0.1 && Math.abs(deltaYaw) > 1.5F;
31 | final boolean invalidPitch = pitchAccel < 0.1 && Math.abs(deltaPitch) > 1.5F;
32 |
33 | final String info = String.format(
34 | "dY=%.2f, dP=%.2f, yA=%.2f, pA=%.2f",
35 | deltaYaw, deltaPitch, yawAccel, pitchAccel
36 | );
37 |
38 | debug(info);
39 |
40 | if ((invalidPitch || invalidYaw) && !exempt) {
41 | if (++buffer > 8) {
42 | fail(info);
43 | }
44 | } else {
45 | buffer -= buffer > 0 ? 1 : 0;
46 | }
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/combat/aimassist/AimAssistD.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.combat.aimassist;
2 |
3 | import ai.medusa.anticheat.data.processor.RotationProcessor;
4 | import ai.medusa.anticheat.check.Check;
5 | import ai.medusa.api.check.CheckInfo;
6 | import ai.medusa.anticheat.data.PlayerData;
7 | import ai.medusa.anticheat.packet.Packet;
8 | import ai.medusa.anticheat.util.MathUtil;
9 |
10 | /**
11 | * Created on Package xyz.elevated.frequency.check.impl.aimassist by Elevated.
12 | *
13 | * This is one of the most powerful aim checks on Medusa. Just to clarify, it was created by Elevated, and taken from
14 | * Frequency with permission. This check makes sure each rotation has a GCD (greatest common divisor). Because of
15 | * how sensitivity works in Minecraft, every rotation is divisible by a specific constant. Because of this, I have
16 | * been able to get sensitivity from rotations in RotationProcesssor. If the rotation does not have a constant, this
17 | * check flags.
18 | *
19 | * @see RotationProcessor
20 | *
21 | * https://github.com/ElevatedDev/Frequency/blob/master/src/main/java/xyz/elevated/frequency/check/impl/aimassist/AimAssistE.java
22 | */
23 |
24 | @CheckInfo(name = "AimAssist (D)", description = "Checks for a divisor in the rotation.")
25 | public final class AimAssistD extends Check {
26 |
27 | private float lastDeltaYaw, lastDeltaPitch;
28 |
29 | public AimAssistD(final PlayerData data) {
30 | super(data);
31 | }
32 |
33 | @Override
34 | public void handle(final Packet packet) {
35 | if (packet.isRotation()) {
36 | if (data.getRotationProcessor().isCinematic()) return;
37 |
38 | final float deltaYaw = data.getRotationProcessor().getDeltaYaw() % 360F;
39 | final float deltaPitch = data.getRotationProcessor().getDeltaPitch();
40 |
41 | final double divisorYaw = MathUtil.getGcd((long) (deltaYaw * MathUtil.EXPANDER), (long) (lastDeltaYaw * MathUtil.EXPANDER));
42 | final double divisorPitch = MathUtil.getGcd((long) (deltaPitch * MathUtil.EXPANDER), (long) (lastDeltaPitch * MathUtil.EXPANDER));
43 |
44 | final double constantYaw = divisorYaw / MathUtil.EXPANDER;
45 | final double constantPitch = divisorPitch / MathUtil.EXPANDER;
46 |
47 | final double currentX = deltaYaw / constantYaw;
48 | final double currentY = deltaPitch / constantPitch;
49 |
50 | final double previousX = lastDeltaYaw / constantYaw;
51 | final double previousY = lastDeltaPitch / constantPitch;
52 |
53 | if (deltaYaw > 0.0 && deltaPitch > 0.0 && deltaYaw < 20.f && deltaPitch < 20.f) {
54 | final double moduloX = currentX % previousX;
55 | final double moduloY = currentY % previousY;
56 |
57 | final double floorModuloX = Math.abs(Math.floor(moduloX) - moduloX);
58 | final double floorModuloY = Math.abs(Math.floor(moduloY) - moduloY);
59 |
60 | final boolean invalidX = moduloX > 90.d && floorModuloX > 0.1;
61 | final boolean invalidY = moduloY > 90.d && floorModuloY > 0.1;
62 |
63 | final String info = String.format(
64 | "mx=%.2f, my=%.2f, fmx=%.2f, fmy=%.2f",
65 | moduloX, moduloY, floorModuloX, floorModuloY
66 | );
67 |
68 | debug(info);
69 |
70 | if (invalidX && invalidY) {
71 | if (++buffer > 6) {
72 | fail(info);
73 | }
74 | } else {
75 | buffer -= buffer > 0 ? 1 : 0;
76 | }
77 | }
78 |
79 | this.lastDeltaYaw = deltaYaw;
80 | this.lastDeltaPitch = deltaPitch;
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/combat/aimassist/AimAssistE.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.combat.aimassist;
2 |
3 | import ai.medusa.api.check.CheckInfo;
4 | import ai.medusa.anticheat.check.Check;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.exempt.type.ExemptType;
7 | import ai.medusa.anticheat.packet.Packet;
8 | import ai.medusa.anticheat.util.MathUtil;
9 |
10 | /**
11 | * Created 12/06/2020 Package ai.medusa.anticheat.check.impl.combat.aimassist by infinitesm
12 | */
13 |
14 | @CheckInfo(name = "AimAssist (E)", description = "Checks for a valid sensitivity in the rotation.")
15 | public final class AimAssistE extends Check {
16 |
17 | public AimAssistE(final PlayerData data) {
18 | super(data);
19 | }
20 |
21 | @Override
22 | public void handle(final Packet packet) {
23 | if (packet.isRotation()) {
24 | final float deltaPitch = data.getRotationProcessor().getDeltaPitch();
25 | final float lastDeltaPitch = data.getRotationProcessor().getLastDeltaPitch();
26 |
27 | final long expandedDeltaPitch = (long) (deltaPitch * MathUtil.EXPANDER);
28 | final long expandedLastDeltaPitch = (long) (lastDeltaPitch * MathUtil.EXPANDER);
29 |
30 | final long gcd = MathUtil.getGcd(expandedDeltaPitch, expandedLastDeltaPitch);
31 |
32 | final boolean exempt = deltaPitch == 0
33 | || lastDeltaPitch == 0
34 | || isExempt(ExemptType.CINEMATIC);
35 |
36 | debug("gcd=" + gcd + " cinematic=" + isExempt(ExemptType.CINEMATIC) + " buf=" + buffer);
37 |
38 | if (!exempt && gcd < 131072L) {
39 | if (++buffer > 10) {
40 | fail("gcd=" + gcd + " buffer=" + buffer);
41 | }
42 | } else {
43 | buffer = Math.max(0, buffer - 1);
44 | }
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/combat/aimassist/AimAssistF.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.combat.aimassist;
2 |
3 | import ai.medusa.anticheat.check.Check;
4 | import ai.medusa.api.check.CheckInfo;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.exempt.type.ExemptType;
7 | import ai.medusa.anticheat.packet.Packet;
8 |
9 | /**
10 | * Created 11/14/2020 Package ai.medusa.anticheat.check.impl.combat.aim by infinitesm
11 | */
12 |
13 | @CheckInfo(name = "AimAssist (F)", description = "Checks for impossible rotation.", experimental = true)
14 | public final class AimAssistF extends Check {
15 |
16 | public AimAssistF(final PlayerData data) {
17 | super(data);
18 | }
19 |
20 | @Override
21 | public void handle(final Packet packet) {
22 | if (packet.isRotation()) {
23 | final float deltaYaw = data.getRotationProcessor().getDeltaYaw();
24 | final float deltaPitch = data.getRotationProcessor().getDeltaPitch();
25 |
26 | debug("tp=" + isExempt(ExemptType.TELEPORT));
27 |
28 | if (deltaPitch == 0 && deltaYaw == 0 && !isExempt(ExemptType.TELEPORT)) fail();
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/combat/aimassist/AimAssistG.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.combat.aimassist;
2 |
3 | import ai.medusa.api.check.CheckInfo;
4 | import ai.medusa.anticheat.check.Check;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.exempt.type.ExemptType;
7 | import ai.medusa.anticheat.packet.Packet;
8 | import ai.medusa.anticheat.util.MathUtil;
9 | import ai.medusa.anticheat.util.type.EvictingList;
10 |
11 | /**
12 | * Created on 11/15/2020 Package ai.medusa.anticheat.check.impl.combat.aim by infinitesm
13 | */
14 |
15 | @CheckInfo(name = "AimAssist (G)" , description = "Checks for extremely smooth rotations.", experimental = true)
16 | public final class AimAssistG extends Check {
17 |
18 | private final EvictingList yawAccelSamples = new EvictingList<>(20);
19 | private final EvictingList pitchAccelSamples = new EvictingList<>(20);
20 |
21 | public AimAssistG(final PlayerData data) {
22 | super(data);
23 | }
24 |
25 | @Override
26 | public void handle(final Packet packet) {
27 | if (packet.isRotation() && !isExempt(ExemptType.CINEMATIC)) {
28 | final float yawAccel = data.getRotationProcessor().getJoltYaw();
29 | final float pitchAccel = data.getRotationProcessor().getJoltPitch();
30 |
31 | yawAccelSamples.add(yawAccel);
32 | pitchAccelSamples.add(pitchAccel);
33 |
34 | if (yawAccelSamples.isFull() && pitchAccelSamples.isFull()) {
35 | final double yawAccelAverage = MathUtil.getAverage(yawAccelSamples);
36 | final double pitchAccelAverage = MathUtil.getAverage(pitchAccelSamples);
37 |
38 | final double yawAccelDeviation = MathUtil.getStandardDeviation(yawAccelSamples);
39 | final double pitchAccelDeviation = MathUtil.getStandardDeviation(pitchAccelSamples);
40 |
41 | final boolean exemptRotation = data.getRotationProcessor().getDeltaYaw() < 1.5F;
42 |
43 | final boolean averageInvalid = yawAccelAverage < 1 || pitchAccelAverage < 1 && !exemptRotation;
44 | final boolean deviationInvalid = yawAccelDeviation < 5 && pitchAccelDeviation > 5 && !exemptRotation;
45 |
46 | debug(String.format(
47 | "yaa=%.2f, paa=%.2f, yad=%.2f, pad=%.2f, buf=%.2f",
48 | yawAccelAverage, pitchAccelAverage, yawAccelDeviation, pitchAccelDeviation, buffer
49 | ));
50 |
51 | if (averageInvalid && deviationInvalid) {
52 | buffer = Math.min(buffer + 1, 20);
53 | if (buffer > 8) {
54 | fail(String.format(
55 | "yaa=%.2f, paa=%.2f, yad=%.2f, pad=%.2f",
56 | yawAccelAverage, pitchAccelAverage, yawAccelDeviation, pitchAccelDeviation
57 | ));
58 | }
59 | } else {
60 | buffer -= buffer > 0 ? 0.75 : 0;
61 | }
62 | }
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/combat/aimassist/AimAssistH.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.combat.aimassist;
2 |
3 | import ai.medusa.api.check.CheckInfo;
4 | import ai.medusa.anticheat.check.Check;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.exempt.type.ExemptType;
7 | import ai.medusa.anticheat.packet.Packet;
8 | import ai.medusa.anticheat.util.MathUtil;
9 | import ai.medusa.anticheat.util.type.EvictingList;
10 | import org.bukkit.Location;
11 | import org.bukkit.entity.Entity;
12 | import org.bukkit.entity.Player;
13 | import org.bukkit.util.Vector;
14 |
15 | @CheckInfo(name = "AimAssist (H)", description = "Checks for a generic flaw in aim assistance tools.", experimental = true)
16 | public class AimAssistH extends Check {
17 |
18 | private final EvictingList differenceSamples = new EvictingList<>(25);
19 |
20 | public AimAssistH(final PlayerData data) {
21 | super(data);
22 | }
23 |
24 | @Override
25 | public void handle(final Packet packet) {
26 | if (packet.isRotation() && isExempt(ExemptType.COMBAT)) {
27 | final Player player = data.getPlayer();
28 | final Entity target = data.getCombatProcessor().getTarget();
29 |
30 | if (target != null) {
31 | final Location origin = player.getLocation().clone();
32 | final Vector end = target.getLocation().clone().toVector();
33 |
34 | final float optimalYaw = origin.setDirection(end.subtract(origin.toVector())).getYaw() % 360F;
35 | final float rotationYaw = data.getRotationProcessor().getYaw();
36 | final float deltaYaw = data.getRotationProcessor().getDeltaYaw();
37 | final float fixedRotYaw = (rotationYaw % 360F + 360F) % 360F;
38 |
39 | final double difference = Math.abs(fixedRotYaw - optimalYaw);
40 |
41 | if (deltaYaw > 3f) {
42 | differenceSamples.add(difference);
43 | }
44 | if (differenceSamples.isFull()) {
45 | final double average = MathUtil.getAverage(differenceSamples);
46 | final double deviation = MathUtil.getStandardDeviation(differenceSamples);
47 |
48 | final boolean invalid = average < 7 && deviation < 12;
49 |
50 | if (invalid) {
51 | if (++buffer > 15) {
52 | fail(String.format("dev=%.2f, avg=%.2f, buf=%.2f", deviation, average, buffer));
53 | }
54 | } else {
55 | buffer -= buffer > 0 ? 1 : 0;
56 | }
57 |
58 | debug("avg=" + average + " deviation=" + deviation + " buf=" + buffer);
59 | }
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/combat/aimassist/AimAssistI.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.combat.aimassist;
2 |
3 | import ai.medusa.api.check.CheckInfo;
4 | import ai.medusa.anticheat.check.Check;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.packet.Packet;
7 |
8 | @CheckInfo(name = "AimAssist (I)", description = "Checks for snappy rotations.", experimental = true)
9 | public class AimAssistI extends Check {
10 |
11 | private float lastDeltaYaw, lastLastDeltaYaw;
12 |
13 | public AimAssistI(final PlayerData data) {
14 | super(data);
15 | }
16 |
17 | @Override
18 | public void handle(final Packet packet) {
19 | if (packet.isRotation()) {
20 | final float deltaYaw = data.getRotationProcessor().getDeltaYaw();
21 |
22 | debug("dy=" + deltaYaw + " ldy=" + lastDeltaYaw + " lldy=" + lastLastDeltaYaw);
23 |
24 | if (deltaYaw < 2F && lastDeltaYaw > 30F && lastLastDeltaYaw < 2F) {
25 | final double low = (deltaYaw + lastLastDeltaYaw) / 2;
26 | final double high = lastDeltaYaw;
27 |
28 | fail(String.format("low=%.2f, high=%.2f", low, high));
29 | }
30 |
31 | lastLastDeltaYaw = lastDeltaYaw;
32 | lastDeltaYaw = deltaYaw;
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/combat/autoclicker/AutoClickerA.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.combat.autoclicker;
2 |
3 | import ai.medusa.anticheat.check.Check;
4 | import ai.medusa.api.check.CheckInfo;
5 | import ai.medusa.anticheat.config.ConfigValue;
6 | import ai.medusa.anticheat.data.PlayerData;
7 | import ai.medusa.anticheat.exempt.type.ExemptType;
8 | import ai.medusa.anticheat.packet.Packet;
9 | import io.github.retrooper.packetevents.packetwrappers.play.in.useentity.WrappedPacketInUseEntity;
10 |
11 | /**
12 | * Created on 10/24/2020 Package ai.medusa.anticheat.check.impl.combat.autoclicker by infinitesm
13 | */
14 |
15 | @CheckInfo(name = "AutoClicker (A)", description = "Checks for attack speed.")
16 | public final class AutoClickerA extends Check {
17 |
18 | private int ticks, cps;
19 | private static final ConfigValue maxCps = new ConfigValue(ConfigValue.ValueType.INTEGER, "max-cps");
20 |
21 | public AutoClickerA(final PlayerData data) {
22 | super(data);
23 | }
24 |
25 | @Override
26 | public void handle(final Packet packet) {
27 | if (packet.isFlying()) {
28 | if (++ticks >= 25) {
29 | debug("cps=" + cps);
30 | if (cps > maxCps.getInt() && !isExempt(ExemptType.AUTO_CLICKER)) {
31 | fail("cps=" + cps);
32 | }
33 | ticks = cps = 0;
34 | }
35 | } else if (packet.isUseEntity()) {
36 | final WrappedPacketInUseEntity wrapper = new WrappedPacketInUseEntity(packet.getRawPacket());
37 | if (wrapper.getAction() == WrappedPacketInUseEntity.EntityUseAction.ATTACK) {
38 | ++cps;
39 | }
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/combat/autoclicker/AutoClickerB.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.combat.autoclicker;
2 |
3 | import ai.medusa.anticheat.check.Check;
4 | import ai.medusa.api.check.CheckInfo;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.exempt.type.ExemptType;
7 | import ai.medusa.anticheat.packet.Packet;
8 | import ai.medusa.anticheat.util.MathUtil;
9 |
10 | import java.util.ArrayDeque;
11 |
12 | /**
13 | * Created on 12/30/2020 Package ai.medusa.anticheat.check.impl.combat.autoclicker by infinitesm
14 | */
15 | @CheckInfo(name = "AutoClicker (B)", description = "Simple change in statistics check.")
16 | public final class AutoClickerB extends Check {
17 |
18 | private int ticks;
19 | private double lastDev, lastSkew, lastKurt;
20 | private ArrayDeque samples = new ArrayDeque<>();
21 |
22 | public AutoClickerB(final PlayerData data) {
23 | super(data);
24 | }
25 |
26 | @Override
27 | public void handle(final Packet packet) {
28 | if (packet.isArmAnimation() && !isExempt(ExemptType.AUTO_CLICKER)) {
29 | if (ticks < 4) {
30 | samples.add(ticks);
31 | }
32 |
33 | if (samples.size() == 120) {
34 | final double deviation = MathUtil.getStandardDeviation(samples);
35 | final double skewness = MathUtil.getSkewness(samples);
36 | final double kurtosis = MathUtil.getKurtosis(samples);
37 |
38 | final double deltaDeviation = Math.abs(deviation - lastDev);
39 | final double deltaSkewness = Math.abs(skewness - lastSkew);
40 | final double deltaKurtosis = Math.abs(kurtosis - lastKurt);
41 |
42 | debug("dd=" + deltaDeviation + " ds=" + deltaSkewness + " dk=" + deltaKurtosis);
43 |
44 | if (deltaDeviation < 0.01 || deltaSkewness < 0.01 || deltaKurtosis < 0.01) {
45 | if ((buffer += 10) > 50) {
46 | fail("dd=" + deltaDeviation + " ds=" + deltaSkewness + " dk=" + deltaKurtosis);
47 | }
48 | } else {
49 | buffer = Math.max(buffer - 8, 0);
50 | }
51 |
52 | lastDev = deviation;
53 | lastSkew = skewness;
54 | lastKurt = kurtosis;
55 |
56 | samples.clear();
57 | }
58 |
59 | ticks = 0;
60 | } else if (packet.isFlying()) {
61 | ++ticks;
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/combat/autoclicker/AutoClickerC.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.combat.autoclicker;
2 |
3 | import ai.medusa.anticheat.check.Check;
4 | import ai.medusa.api.check.CheckInfo;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.exempt.type.ExemptType;
7 | import ai.medusa.anticheat.packet.Packet;
8 | import ai.medusa.anticheat.util.MathUtil;
9 |
10 | import java.util.ArrayDeque;
11 |
12 | @CheckInfo(name = "AutoClicker (C)", experimental = true, description = "Checks for rounded(ish) CPS.")
13 | public final class AutoClickerC extends Check {
14 |
15 | private final ArrayDeque samples = new ArrayDeque<>();
16 | private int ticks;
17 |
18 | public AutoClickerC(final PlayerData data) {
19 | super(data);
20 | }
21 |
22 | @Override
23 | public void handle(final Packet packet) {
24 | if (packet.isArmAnimation() && !isExempt(ExemptType.AUTO_CLICKER)) {
25 |
26 | if (ticks < 4) {
27 | samples.add(ticks);
28 | }
29 |
30 | if (samples.size() == 50) {
31 |
32 | final double cps = MathUtil.getCps(samples);
33 | final double difference = Math.abs(Math.round(cps) - cps);
34 |
35 | debug("diff=" + difference + " cps=" + cps + " buf=" + buffer);
36 |
37 | if (difference < 0.001) {
38 | if ((buffer += 10) > 25) {
39 | fail("diff=" + difference);
40 | }
41 | } else {
42 | buffer = Math.max(buffer - 8, 0);
43 | }
44 |
45 | samples.clear();
46 | }
47 |
48 | ticks = 0;
49 | } else if (packet.isFlying()) {
50 | ++ticks;
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/combat/autoclicker/AutoClickerD.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.combat.autoclicker;
2 |
3 | import ai.medusa.anticheat.check.Check;
4 | import ai.medusa.api.check.CheckInfo;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.exempt.type.ExemptType;
7 | import ai.medusa.anticheat.packet.Packet;
8 | import ai.medusa.anticheat.util.MathUtil;
9 | import ai.medusa.anticheat.util.type.Pair;
10 |
11 | import java.util.ArrayDeque;
12 | import java.util.List;
13 |
14 | /**
15 | * Created by Elevated.
16 | * @link https://github.com/ElevatedDev/Frequency/blob/master/src/main/java/xyz/elevated/frequency/check/impl/autoclicker/AutoClickerE.java
17 | */
18 |
19 | @CheckInfo(name = "AutoClicker (D)", experimental = true, description = "Checks for consistency.")
20 | public final class AutoClickerD extends Check {
21 |
22 | private final ArrayDeque samples = new ArrayDeque<>();
23 | private int ticks;
24 |
25 | public AutoClickerD(final PlayerData data) {
26 | super(data);
27 | }
28 |
29 | @Override
30 | public void handle(final Packet packet) {
31 | if (packet.isArmAnimation() && !isExempt(ExemptType.AUTO_CLICKER)) {
32 | if (ticks < 4) {
33 | samples.add(ticks);
34 | }
35 |
36 | if (samples.size() == 20) {
37 | final Pair, List> outlierPair = MathUtil.getOutliers(samples);
38 |
39 | final int outliers = outlierPair.getX().size() + outlierPair.getY().size();
40 | final int duplicates = (int) (samples.size() - samples.stream().distinct().count());
41 |
42 | debug("outliers=" + outliers + " dupl=" + duplicates);
43 |
44 | if (outliers < 2 && duplicates > 16) {
45 | if ((buffer += 10) > 50) {
46 | fail("outliers=" + outliers + " dupl=" + duplicates);
47 | }
48 | } else {
49 | buffer = Math.max(buffer - 8, 0);
50 | }
51 | samples.clear();
52 | }
53 |
54 | ticks = 0;
55 | } else if (packet.isFlying()) {
56 | ++ticks;
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/combat/hitbox/HitBoxA.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.combat.hitbox;
2 |
3 | import ai.medusa.api.check.CheckInfo;
4 | import ai.medusa.anticheat.Medusa;
5 | import ai.medusa.anticheat.check.Check;
6 | import ai.medusa.anticheat.config.ConfigValue;
7 | import ai.medusa.anticheat.data.PlayerData;
8 | import ai.medusa.anticheat.packet.Packet;
9 | import ai.medusa.anticheat.util.PlayerUtil;
10 | import ai.medusa.anticheat.util.type.BoundingBox;
11 | import ai.medusa.anticheat.util.type.RayTrace;
12 | import io.github.retrooper.packetevents.packetwrappers.play.in.useentity.WrappedPacketInUseEntity;
13 | import org.bukkit.GameMode;
14 | import org.bukkit.Location;
15 | import org.bukkit.entity.Entity;
16 | import org.bukkit.entity.Player;
17 | import org.bukkit.entity.Villager;
18 | import org.bukkit.util.NumberConversions;
19 |
20 | /**
21 | * Created on 10/26/2020 Package ai.medusa.anticheat.check.impl.combat.reach by infinitesm
22 | */
23 |
24 | @CheckInfo(name = "HitBox (A)", experimental = true, description = "Checks for the angle of the attack.")
25 | public final class HitBoxA extends Check {
26 |
27 | private static final ConfigValue maxLatency = new ConfigValue(ConfigValue.ValueType.LONG, "max-latency");
28 | private static final ConfigValue raytraceSensitivityLevel = new ConfigValue(
29 | ConfigValue.ValueType.INTEGER, "raytrace-sensitivity-level"
30 | );
31 |
32 | public HitBoxA(final PlayerData data) {
33 | super(data);
34 | }
35 |
36 | @Override
37 | public void handle(final Packet packet) {
38 | if (packet.isUseEntity()) {
39 | final WrappedPacketInUseEntity wrapper = new WrappedPacketInUseEntity(packet.getRawPacket());
40 |
41 | final Entity target = data.getCombatProcessor().getTarget();
42 | final Entity lastTarget = data.getCombatProcessor().getLastTarget();
43 |
44 | if (wrapper.getAction() != WrappedPacketInUseEntity.EntityUseAction.ATTACK
45 | || data.getPlayer().getGameMode() != GameMode.SURVIVAL
46 | || !(target instanceof Player || target instanceof Villager)
47 | || target != lastTarget
48 | || !data.getTargetLocations().isFull()
49 | || PlayerUtil.getPing(data.getPlayer()) > (maxLatency.getLong() < 0 ? Integer.MAX_VALUE : maxLatency.getLong())) return;
50 |
51 | final int ticks = Medusa.INSTANCE.getTickManager().getTicks();
52 | final int pingTicks = NumberConversions.floor(data.getActionProcessor().getPing() / 50.0) + 3;
53 |
54 | final RayTrace rayTrace = new RayTrace(data.getPlayer());
55 |
56 | final int collided = (int) data.getTargetLocations().stream()
57 | .filter(pair -> Math.abs(ticks - pair.getY() - pingTicks) < 3)
58 | .filter(pair -> {
59 | final Location location = pair.getX();
60 | final BoundingBox boundingBox = new BoundingBox(
61 | location.getX() - 0.4,
62 | location.getX() + 0.4,
63 | location.getY(),
64 | location.getY() + 1.9,
65 | location.getZ() - 0.4,
66 | location.getZ() + 0.4
67 | );
68 |
69 | return boundingBox.collidesD(rayTrace, 0, 6) != 10;
70 | }).count();
71 |
72 | final int sensitivity = raytraceSensitivityLevel.getInt();
73 |
74 | debug("collided=" + collided + " sens=" + sensitivity + " buf=" + buffer);
75 |
76 | if (collided <= sensitivity) {
77 | if (++buffer > 10) {
78 | fail("collided=" + collided + " buffer=" + buffer);
79 | }
80 | } else {
81 | buffer -= buffer > 0 ? 1 : 0;
82 | }
83 | }
84 | }
85 | }
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/combat/killaura/KillAuraA.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.combat.killaura;
2 |
3 | import ai.medusa.anticheat.check.Check;
4 | import ai.medusa.api.check.CheckInfo;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.packet.Packet;
7 |
8 | /**
9 | * Created on 10/24/2020 Package ai.medusa.anticheat.check.impl.combat.killaura by infinitesm
10 | */
11 |
12 | @CheckInfo(name = "KillAura (A)", description = "Checks for packet order.")
13 | public final class KillAuraA extends Check {
14 |
15 | private boolean usedEntity;
16 | private long lastUseEntityTime;
17 |
18 | public KillAuraA(final PlayerData data) {
19 | super(data);
20 | }
21 |
22 | @Override
23 | public void handle(final Packet packet) {
24 | if (packet.isUseEntity()) {
25 | usedEntity = true;
26 | lastUseEntityTime = now();
27 | } else if (packet.isFlying()) {
28 | if (usedEntity) {
29 | final long delay = now() - lastUseEntityTime;
30 | final boolean invalid = !data.getActionProcessor().isLagging() && delay > 15;
31 |
32 | debug("delay=" + delay);
33 | if (invalid) {
34 | if (++buffer > 2) {
35 | fail(String.format("delay=%d", delay));
36 | }
37 | } else {
38 | buffer = Math.max(buffer - 0.15, 0);
39 | }
40 | }
41 | usedEntity = false;
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/combat/killaura/KillAuraB.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.combat.killaura;
2 |
3 | import ai.medusa.anticheat.check.Check;
4 | import ai.medusa.api.check.CheckInfo;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.exempt.type.ExemptType;
7 | import ai.medusa.anticheat.packet.Packet;
8 | import org.bukkit.entity.Entity;
9 | import org.bukkit.entity.EntityType;
10 |
11 | /**
12 | * Created on 10/24/2020 Package ai.medusa.anticheat.check.impl.combat.killaura by infinitesm
13 | */
14 |
15 | @CheckInfo(name = "KillAura (B)", description = "Checks for KeepSprint modules.")
16 | public final class KillAuraB extends Check {
17 |
18 | public KillAuraB(final PlayerData data) {
19 | super(data);
20 | }
21 |
22 | @Override
23 | public void handle(final Packet packet) {
24 | if (packet.isPosLook()) {
25 | if (data.getExemptProcessor().isExempt(ExemptType.COMBAT)) {
26 | final boolean sprinting = data.getActionProcessor().isSprinting();
27 | final double deltaXZ = data.getPositionProcessor().getDeltaXZ();
28 | final double lastDeltaXZ = data.getPositionProcessor().getLastDeltaXZ();
29 |
30 | final double acceleration = Math.abs(deltaXZ - lastDeltaXZ);
31 | final long clickDelay = data.getClickProcessor().getDelay();
32 | final Entity target = data.getCombatProcessor().getTarget();
33 |
34 | final boolean invalid = acceleration < 0.0025 &&
35 | deltaXZ > 0.22 &&
36 | sprinting &&
37 | clickDelay < 250 &&
38 | target.getType() == EntityType.PLAYER;
39 |
40 | debug("a=" + acceleration + " dxz=" + deltaXZ + " cd=" + clickDelay);
41 | if (invalid) {
42 | if (++buffer > 5) {
43 | fail(String.format("acceleration=%.6f", acceleration));
44 | buffer = Math.min(10, buffer);
45 | }
46 | } else {
47 | buffer = Math.max(buffer - 0.25, 0);
48 | }
49 | }
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/combat/killaura/KillAuraC.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.combat.killaura;
2 |
3 | import ai.medusa.anticheat.check.Check;
4 | import ai.medusa.api.check.CheckInfo;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.packet.Packet;
7 |
8 | /**
9 | * Created on 10/24/2020 Package ai.medusa.anticheat.check.impl.combat.killaura by infinitesm
10 | */
11 |
12 | @CheckInfo(name = "KillAura (C)", description = "Checks for multi-aura.")
13 | public final class KillAuraC extends Check {
14 |
15 | public KillAuraC(final PlayerData data) {
16 | super(data);
17 | }
18 |
19 | @Override
20 | public void handle(final Packet packet) {
21 | if (packet.isUseEntity()) {
22 | final int targets = data.getCombatProcessor().getCurrentTargets();
23 |
24 | debug("tg="+ targets);
25 | if (targets > 1) fail("tg=" + targets);
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/combat/killaura/KillAuraD.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.combat.killaura;
2 |
3 | import ai.medusa.anticheat.check.Check;
4 | import ai.medusa.api.check.CheckInfo;
5 |
6 | import ai.medusa.anticheat.data.PlayerData;
7 | import ai.medusa.anticheat.packet.Packet;
8 | import io.github.retrooper.packetevents.packetwrappers.play.in.useentity.WrappedPacketInUseEntity;
9 | import org.bukkit.Location;
10 |
11 | /**
12 | * Created on 10/24/2020 Package ai.medusa.anticheat.check.impl.combat.killaura by infinitesm
13 | */
14 |
15 | @CheckInfo(name = "KillAura (D)", description = "Checks for high accuracy.")
16 | public final class KillAuraD extends Check {
17 |
18 | private int hits, swings;
19 | private Location lastLocation;
20 | private Float lastYaw, lastPitch;
21 |
22 | public KillAuraD(final PlayerData data) {
23 | super(data);
24 | }
25 |
26 | @Override
27 | public void handle(final Packet packet) {
28 | if (packet.isUseEntity()) {
29 |
30 | final WrappedPacketInUseEntity wrapper = new WrappedPacketInUseEntity(packet.getRawPacket());
31 | final float yaw = data.getRotationProcessor().getYaw() % 360F;
32 | final float pitch = data.getRotationProcessor().getPitch();
33 | final boolean isMoving = data.getPositionProcessor().getDeltaXZ() != 0;
34 | if (wrapper.getEntity() != null) {
35 | if (lastLocation != null && lastYaw != null && lastPitch != null) {
36 | if (wrapper.getEntity().getLocation().distance(lastLocation) > 0.1
37 | && lastYaw != yaw && lastPitch != pitch && isMoving
38 | && wrapper.getEntity().getWorld() == data.getPlayer().getWorld()) {
39 | ++hits;
40 | }
41 | }
42 | lastLocation = wrapper.getEntity().getLocation();
43 | }
44 | lastYaw = yaw;
45 | lastPitch = pitch;
46 | } else if (packet.isArmAnimation()) {
47 | debug("swings=" + swings);
48 | if (++swings >= 50) {
49 | final double accuracy = hits/swings;
50 | debug("accuracy=" + accuracy);
51 | if (hits > 40) {
52 | if ((buffer += 10) > 20) {
53 | fail("accuracy=" + accuracy);
54 | }
55 | } else {
56 | buffer = Math.max(buffer - 2, 0);
57 | }
58 | hits = swings = 0;
59 | }
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/combat/killaura/KillAuraE.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.combat.killaura;
2 |
3 | import ai.medusa.anticheat.check.Check;
4 | import ai.medusa.api.check.CheckInfo;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.packet.Packet;
7 | import io.github.retrooper.packetevents.packetwrappers.play.in.useentity.WrappedPacketInUseEntity;
8 |
9 | /**
10 | * Created on 07/01/2020 Package luna.anticheat.checks.player.badpackets by infinitesm
11 | */
12 | @CheckInfo(name = "KillAura (E)", description = "Checks for no swing modules.")
13 | public final class KillAuraE extends Check {
14 |
15 | private int hits;
16 |
17 | public KillAuraE(final PlayerData data) {
18 | super(data);
19 | }
20 |
21 | @Override
22 | public void handle(final Packet packet) {
23 | if (packet.isUseEntity()) {
24 | final WrappedPacketInUseEntity wrapper = new WrappedPacketInUseEntity(packet.getRawPacket());
25 | if (wrapper.getAction() == WrappedPacketInUseEntity.EntityUseAction.ATTACK) {
26 | debug("hits=" + hits);
27 | if (++hits > 2) {
28 | fail("ticks=" + hits);
29 | }
30 | }
31 | } else if (packet.isArmAnimation()) {
32 | hits = 0;
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/combat/killaura/KillAuraF.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.combat.killaura;
2 |
3 | import ai.medusa.api.check.CheckInfo;
4 | import ai.medusa.anticheat.check.Check;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.packet.Packet;
7 | import org.bukkit.Location;
8 | import org.bukkit.entity.Entity;
9 | import org.bukkit.entity.Player;
10 |
11 | @CheckInfo(name = "KillAura (F)", experimental = true, description = "Checks for hit occlusion (wallhit).")
12 | public final class KillAuraF extends Check {
13 |
14 | private Location lastAttackerLocation;
15 | private float lastYaw, lastPitch;
16 |
17 | public KillAuraF(final PlayerData data) {
18 | super(data);
19 | }
20 |
21 | @Override
22 | public void handle(final Packet packet) {
23 | if (packet.isUseEntity()) {
24 | final Entity target = data.getCombatProcessor().getTarget();
25 | final Player attacker = data.getPlayer();
26 |
27 | if (target == null || attacker == null) return;
28 | if (target.getWorld() != attacker.getWorld()) return;
29 |
30 | final Location attackerLocation = attacker.getLocation();
31 |
32 | final float yaw = data.getRotationProcessor().getYaw() % 360F;
33 | final float pitch = data.getRotationProcessor().getPitch();
34 |
35 | if (lastAttackerLocation != null) {
36 | final boolean check = yaw != lastYaw &&
37 | pitch != lastPitch &&
38 | attackerLocation.distance(lastAttackerLocation) > 0.1;
39 |
40 | debug("buffer=" + buffer);
41 |
42 | if (check && !attacker.hasLineOfSight(target)) {
43 | if ((buffer += 10) > 50) {
44 | fail("buffer=" + buffer);
45 | }
46 | } else {
47 | buffer = Math.max(buffer - 20, 0);
48 | }
49 | }
50 |
51 | lastAttackerLocation = attacker.getLocation();
52 |
53 | lastYaw = yaw;
54 | lastPitch = pitch;
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/combat/killaura/KillAuraG.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.combat.killaura;
2 |
3 | import ai.medusa.api.check.CheckInfo;
4 | import ai.medusa.anticheat.check.Check;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.exempt.type.ExemptType;
7 | import ai.medusa.anticheat.packet.Packet;
8 |
9 | @CheckInfo(name = "KillAura (G)", description = "Checks for large head movements without decelerating.", experimental = true)
10 | public class KillAuraG extends Check {
11 |
12 | public KillAuraG(final PlayerData data) {
13 | super(data);
14 | }
15 |
16 | @Override
17 | public void handle(final Packet packet) {
18 | if (packet.isPosLook() && isExempt(ExemptType.COMBAT)) {
19 | final double deltaXz = data.getPositionProcessor().getDeltaXZ();
20 | final double lastDeltaXz = data.getPositionProcessor().getLastDeltaXZ();
21 | final double accelXz = Math.abs(deltaXz - lastDeltaXz);
22 |
23 | final float deltaYaw = data.getRotationProcessor().getDeltaYaw();
24 |
25 | debug("accel=" + accelXz + " dY=" + deltaYaw);
26 |
27 | if (deltaYaw > 35 && accelXz < 0.00001) {
28 | fail(String.format("accel=%.6f, dY=%.2f", accelXz, deltaYaw));
29 | }
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/combat/reach/ReachA.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.combat.reach;
2 |
3 | import ai.medusa.api.check.CheckInfo;
4 | import ai.medusa.anticheat.Medusa;
5 | import ai.medusa.anticheat.check.Check;
6 | import ai.medusa.anticheat.config.ConfigValue;
7 | import ai.medusa.anticheat.data.PlayerData;
8 | import ai.medusa.anticheat.packet.Packet;
9 | import ai.medusa.anticheat.util.HitboxExpansion;
10 | import ai.medusa.anticheat.util.PlayerUtil;
11 | import io.github.retrooper.packetevents.packetwrappers.play.in.useentity.WrappedPacketInUseEntity;
12 | import org.bukkit.GameMode;
13 | import org.bukkit.entity.Entity;
14 | import org.bukkit.entity.LivingEntity;
15 | import org.bukkit.util.NumberConversions;
16 | import org.bukkit.util.Vector;
17 |
18 | /**
19 | * Created on 10/26/2020 Package ai.medusa.anticheat.check.impl.combat.reach by infinitesm
20 | *
21 | * Fix stupid HitboxExpansion using NMS.
22 | */
23 |
24 | @CheckInfo(name = "Reach (A)", description = "Checks for attacking distance.")
25 | public final class ReachA extends Check {
26 |
27 | private static final ConfigValue maxReach = new ConfigValue(ConfigValue.ValueType.DOUBLE, "max-reach");
28 | private static final ConfigValue maxThreshold = new ConfigValue(ConfigValue.ValueType.DOUBLE, "max-threshold");
29 | private static final ConfigValue thresholdDecay = new ConfigValue(ConfigValue.ValueType.DOUBLE, "threshold-decay");
30 | private static final ConfigValue maxLatency = new ConfigValue(ConfigValue.ValueType.LONG, "max-latency");
31 |
32 | public ReachA(final PlayerData data) {
33 | super(data);
34 | }
35 |
36 | @Override
37 | public void handle(final Packet packet) {
38 | if (packet.isUseEntity()) {
39 | final WrappedPacketInUseEntity wrapper = new WrappedPacketInUseEntity(packet.getRawPacket());
40 |
41 | final Entity target = data.getCombatProcessor().getTarget();
42 | final Entity lastTarget = data.getCombatProcessor().getLastTarget();
43 |
44 | if (wrapper.getAction() != WrappedPacketInUseEntity.EntityUseAction.ATTACK
45 | || data.getPlayer().getGameMode() != GameMode.SURVIVAL
46 | || !(target instanceof LivingEntity)
47 | || target != lastTarget
48 | || !data.getTargetLocations().isFull()
49 | || PlayerUtil.getPing(data.getPlayer()) > (maxLatency.getLong() < 0 ? Integer.MAX_VALUE : maxLatency.getLong())) return;
50 |
51 | final int ticks = Medusa.INSTANCE.getTickManager().getTicks();
52 | final int pingTicks = NumberConversions.floor(data.getActionProcessor().getPing() / 50.0) + 3;
53 |
54 | final Vector player = data.getPlayer().getLocation().toVector().setY(0);
55 |
56 | final double distance = data.getTargetLocations().stream()
57 | .filter(pair -> Math.abs(ticks - pair.getY() - pingTicks) < 3)
58 | .mapToDouble(pair -> {
59 | final Vector victim = pair.getX().toVector().setY(0);
60 | final double expansion = HitboxExpansion.getExpansion(target);
61 | return player.distance(victim) - expansion;
62 | }).min().orElse(0);
63 |
64 | if (distance == 0) return;
65 |
66 | debug("dist=" + distance + " buffer=" + buffer + " pt=" + pingTicks);
67 | if (distance > maxReach.getDouble()) {
68 | if (++buffer > maxThreshold.getDouble()) {
69 | fail(String.format("reach=%.2f, buffer=%.2f", distance, buffer));
70 | }
71 | } else {
72 | buffer = Math.max(buffer - thresholdDecay.getDouble(), 0);
73 | }
74 | }
75 | }
76 |
77 | }
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/combat/reach/ReachB.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.combat.reach;
2 |
3 | import ai.medusa.anticheat.check.Check;
4 | import ai.medusa.api.check.CheckInfo;
5 | import ai.medusa.anticheat.config.ConfigValue;
6 | import ai.medusa.anticheat.data.PlayerData;
7 | import ai.medusa.anticheat.packet.Packet;
8 | import ai.medusa.anticheat.util.HitboxExpansion;
9 | import ai.medusa.anticheat.util.PlayerUtil;
10 | import io.github.retrooper.packetevents.packetwrappers.play.in.useentity.WrappedPacketInUseEntity;
11 | import org.bukkit.GameMode;
12 | import org.bukkit.util.Vector;
13 |
14 | @CheckInfo(name = "Reach (B)", experimental = true, description = "A basic verbose reach check.")
15 | public final class ReachB extends Check {
16 |
17 | private static final ConfigValue maxLatency = new ConfigValue(ConfigValue.ValueType.LONG, "max-latency");
18 | private static final ConfigValue maxReach = new ConfigValue(ConfigValue.ValueType.DOUBLE, "max-reach");
19 |
20 | public ReachB(final PlayerData data) {
21 | super(data);
22 | }
23 |
24 | @Override
25 | public void handle(final Packet packet) {
26 | if (packet.isUseEntity()) {
27 | final WrappedPacketInUseEntity wrapper = new WrappedPacketInUseEntity(packet.getRawPacket());
28 |
29 | if (data.getPlayer().getGameMode() != GameMode.SURVIVAL) return;
30 | if (PlayerUtil.getPing(data.getPlayer()) > (maxLatency.getLong() < 0 ? Integer.MAX_VALUE : maxLatency.getLong())) return;
31 |
32 | if (wrapper.getAction() == WrappedPacketInUseEntity.EntityUseAction.ATTACK) {
33 | final Vector attacker = data.getPlayer().getLocation().toVector().setY(0);
34 | final Vector victim = data.getCombatProcessor().getTarget().getLocation().toVector().setY(0);
35 |
36 | final double expansion = HitboxExpansion.getExpansion(data.getCombatProcessor().getTarget());
37 | final double distance = attacker.distance(victim) - expansion;
38 |
39 | debug("dist=" + distance + " buf=" + buffer);
40 | if (distance > maxReach.getDouble()) {
41 | if (++buffer > 20) {
42 | buffer = 20;
43 | fail(String.format("reach=%.2f, buffer=%.2f", distance, buffer));
44 | }
45 | } else {
46 | buffer = Math.max(buffer - 4, 0);
47 | }
48 | }
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/combat/velocity/VelocityA.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.combat.velocity;
2 |
3 | import ai.medusa.anticheat.check.Check;
4 | import ai.medusa.api.check.CheckInfo;
5 | import ai.medusa.anticheat.config.ConfigValue;
6 | import ai.medusa.anticheat.data.PlayerData;
7 | import ai.medusa.anticheat.exempt.type.ExemptType;
8 | import ai.medusa.anticheat.packet.Packet;
9 |
10 | /**
11 | * Created on 11/23/2020 Package ai.medusa.anticheat.check.impl.combat.velocity by infinitesm
12 | */
13 | @CheckInfo(name = "Velocity (A)", experimental = true, description = "Checks for vertical velocity.")
14 | public final class VelocityA extends Check {
15 |
16 | private static final ConfigValue minVelPct = new ConfigValue(
17 | ConfigValue.ValueType.INTEGER, "minimum-velocity-percentage"
18 | );
19 | private static final ConfigValue maxVelPct = new ConfigValue(
20 | ConfigValue.ValueType.INTEGER, "maximum-velocity-percentage"
21 | );
22 |
23 | public VelocityA(final PlayerData data) {
24 | super(data);
25 | }
26 |
27 | @Override
28 | public void handle(final Packet packet) {
29 | if (packet.isFlying()) {
30 | if (data.getVelocityProcessor().getTicksSinceVelocity() < 5) {
31 | final double deltaY = data.getPositionProcessor().getDeltaY();
32 | final double velocityY = data.getVelocityProcessor().getVelocityY();
33 |
34 | debug("dy=" + deltaY + " vy=" + velocityY);
35 | if (velocityY > 0) {
36 | final int percentage = (int) Math.round((deltaY * 100.0) / velocityY);
37 |
38 | final boolean exempt = isExempt(
39 | ExemptType.LIQUID, ExemptType.PISTON, ExemptType.CLIMBABLE,
40 | ExemptType.UNDER_BLOCK, ExemptType.TELEPORT, ExemptType.FLYING,
41 | ExemptType.WEB, ExemptType.STEPPED
42 | );
43 |
44 | final boolean invalid = !exempt
45 | && (percentage < minVelPct.getInt() || percentage > maxVelPct.getInt());
46 |
47 | if (invalid) {
48 | if (++buffer > 5) {
49 | buffer = 0;
50 | fail("vy=" + velocityY + " pct=" + percentage);
51 | }
52 | } else {
53 | buffer = 0;
54 | }
55 | }
56 | }
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/movement/fastclimb/FastClimbA.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.movement.fastclimb;
2 |
3 | import ai.medusa.anticheat.check.Check;
4 | import ai.medusa.api.check.CheckInfo;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.exempt.type.ExemptType;
7 | import ai.medusa.anticheat.packet.Packet;
8 | import ai.medusa.anticheat.util.PlayerUtil;
9 | import org.bukkit.potion.PotionEffectType;
10 |
11 | /**
12 | * Created on 10/26/2020 Package ai.medusa.anticheat.check.impl.movement.fastclimb by infinitesm
13 | */
14 |
15 | @CheckInfo(name = "FastClimb (A)", description = "Checks if the player exceeds maximum ladder movement speed.")
16 | public final class FastClimbA extends Check {
17 |
18 | public FastClimbA(final PlayerData data) {
19 | super(data);
20 | }
21 |
22 | @Override
23 | public void handle(final Packet packet) {
24 | if (packet.isPosition() && !isExempt(ExemptType.TELEPORT)) {
25 | final boolean onGround = data.getPositionProcessor().isOnSolidGround();
26 | final boolean onLadder = data.getPositionProcessor().isOnClimbable();
27 | final double deltaY = data.getPositionProcessor().getDeltaY();
28 | final double deltaDeltaY = Math.abs(data.getPositionProcessor().getDeltaY() - data.getPositionProcessor().getLastDeltaY());
29 | final double maximumNonBufferDeltaY = 0.42F + PlayerUtil.getPotionLevel(data.getPlayer(), PotionEffectType.JUMP) * 0.1;
30 | //Creates an easy bypass. Replace this with collisions replaced from MCP.
31 | final boolean invalid = (!onGround && onLadder && deltaDeltaY == 0 && deltaY > 0.1177);
32 | final boolean fuckOff = deltaY > maximumNonBufferDeltaY && onLadder;
33 |
34 | /*
35 | * Using a buffer makes this check VERY easy to bypass.
36 | * This check should flag on the first tick. If you do not flag on the first tick, you run
37 | * the risk of insanely fast FastClimb bypasses. FIX THIS.
38 | */
39 |
40 | debug("dy=" + deltaY);
41 | if (invalid) {
42 | if (++buffer > 2) {
43 | fail(String.format("dy=%.2f > 0.1176", deltaY));
44 | }
45 | } else {
46 | buffer = Math.max(buffer - 0.5, 0);
47 | }
48 |
49 | if (fuckOff) fail(String.format("dy=%.2f > %.2f", deltaY, maximumNonBufferDeltaY));
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/movement/fly/FlyA.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.movement.fly;
2 |
3 | import ai.medusa.api.check.CheckInfo;
4 | import ai.medusa.anticheat.check.Check;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.exempt.type.ExemptType;
7 | import ai.medusa.anticheat.packet.Packet;
8 |
9 | /**
10 | * Created on 11/17/2020 Package ai.medusa.anticheat.check.impl.movement.fly by infinitesm
11 | */
12 |
13 | @CheckInfo(name = "Fly (A)", description = "Checks for gravity.")
14 | public final class FlyA extends Check {
15 |
16 | public FlyA(final PlayerData data) {
17 | super(data);
18 | }
19 |
20 | @Override
21 | public void handle(final Packet packet) {
22 | if (packet.isPosition()) {
23 | final double deltaY = data.getPositionProcessor().getDeltaY();
24 | final double lastDeltaY = data.getPositionProcessor().getLastDeltaY();
25 |
26 | final boolean onGround = data.getPositionProcessor().getAirTicks() <= 5;
27 |
28 | final double prediction = Math.abs((lastDeltaY - 0.08) * 0.98F) < 0.005 ? -0.08 * 0.98F : (lastDeltaY - 0.08) * 0.98F;
29 | final double difference = Math.abs(deltaY - prediction);
30 |
31 | final boolean exempt = isExempt(
32 | ExemptType.TELEPORT, ExemptType.NEAR_VEHICLE, ExemptType.FLYING,
33 | ExemptType.INSIDE_VEHICLE, ExemptType.VELOCITY
34 | );
35 |
36 | final boolean invalid = !exempt
37 | && difference > 0.001D
38 | && !onGround
39 | && !(data.getPositionProcessor().getY() % 0.5 == 0 && data.getPositionProcessor().isOnGround() && lastDeltaY < 0);
40 |
41 | debug("posY=" + data.getPositionProcessor().getY() + " dY=" + deltaY + " at=" + data.getPositionProcessor().getAirTicks());
42 |
43 | if (invalid) {
44 | buffer += buffer < 50 ? 10 : 0;
45 | if (buffer > 20) {
46 | fail(String.format("diff=%.4f, buffer=%.2f, at=%o", difference, buffer, data.getPositionProcessor().getAirTicks()));
47 | }
48 | } else {
49 | buffer = Math.max(buffer - 0.75, 0);
50 | }
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/movement/fly/FlyB.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.movement.fly;
2 |
3 | import ai.medusa.anticheat.check.Check;
4 | import ai.medusa.api.check.CheckInfo;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.exempt.type.ExemptType;
7 | import ai.medusa.anticheat.packet.Packet;
8 |
9 | /**
10 | * Created on 11/17/2020 Package ai.medusa.anticheat.check.impl.movement.fly by infinitesm
11 | */
12 |
13 | @CheckInfo(name = "Fly (B)", description = "Checks for jumping mid-air.")
14 | public final class FlyB extends Check {
15 |
16 | private double lastAcceleration;
17 |
18 | public FlyB(final PlayerData data) {
19 | super(data);
20 | }
21 |
22 | @Override
23 | public void handle(final Packet packet) {
24 | if (packet.isPosition() && !isExempt(ExemptType.TELEPORT)) {
25 | final double acceleration = data.getPositionProcessor().getDeltaY() - data.getPositionProcessor().getLastDeltaY();
26 | final double airTicks = data.getPositionProcessor().getAirTicks();
27 | final double deltaY = data.getPositionProcessor().getDeltaY();
28 |
29 | final boolean invalid = !isExempt(
30 | ExemptType.FLYING, ExemptType.VELOCITY, ExemptType.INSIDE_VEHICLE, ExemptType.NEAR_VEHICLE
31 | ) && lastAcceleration <= 0 && acceleration > 0 && deltaY > 0;
32 |
33 | debug("airTicks=" + airTicks + " accel=" + acceleration);
34 | if (airTicks > 10) {
35 | if (invalid) {
36 | fail(String.format("accel=%.2f, at=%.2f", acceleration, airTicks));
37 | }
38 | }
39 |
40 | lastAcceleration = acceleration;
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/movement/fly/FlyC.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.movement.fly;
2 |
3 | import ai.medusa.anticheat.check.Check;
4 | import ai.medusa.api.check.CheckInfo;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.exempt.type.ExemptType;
7 | import ai.medusa.anticheat.packet.Packet;
8 | import io.github.retrooper.packetevents.packetwrappers.play.in.flying.WrappedPacketInFlying;
9 |
10 | /**
11 | * Created on 11/17/2020 Package ai.medusa.anticheat.check.impl.movement.fly by infinitesm
12 | *
13 | * This is a decent nofall check however it's easily worked around by using the Flying packet and not the Position
14 | * packet to set on ground values. I would use Flying packet for this but it has a lot of false flag so Position
15 | * is the way to go here to be more stable.
16 | */
17 |
18 | @CheckInfo(name = "Fly (C)", description = "Checks for ground-spoof.")
19 | public final class FlyC extends Check {
20 |
21 | public FlyC(final PlayerData data) {
22 | super(data);
23 | }
24 |
25 | @Override
26 | public void handle(final Packet packet) {
27 | if (packet.isPosition()) {
28 | final WrappedPacketInFlying wrapper = new WrappedPacketInFlying(packet.getRawPacket());
29 |
30 | final boolean positionGround = wrapper.getY() % 0.015625 == 0;
31 | final boolean packetGround = wrapper.isOnGround();
32 |
33 | final boolean exempt = isExempt(ExemptType.NEAR_VEHICLE, ExemptType.TELEPORT, ExemptType.CLIMBABLE,
34 | ExemptType.FLYING, ExemptType.JOINED, ExemptType.SLIME);
35 |
36 | debug(positionGround + " " + packetGround + " pos/packet");
37 |
38 | if (!exempt && positionGround != packetGround) {
39 | if (++buffer > 4) {
40 | fail();
41 | }
42 | } else {
43 | buffer = Math.max(buffer - 0.15, 0);
44 | }
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/movement/jesus/JesusA.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.movement.jesus;
2 |
3 | import ai.medusa.anticheat.check.Check;
4 | import ai.medusa.api.check.CheckInfo;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.exempt.type.ExemptType;
7 | import ai.medusa.anticheat.packet.Packet;
8 | import org.bukkit.Material;
9 | import org.bukkit.block.Block;
10 | import java.util.List;
11 | @CheckInfo(name = "Jesus (A)", description = "Checks for water friction.", experimental = true)
12 | public final class JesusA extends Check {
13 |
14 | private int ticks;
15 |
16 | public JesusA(final PlayerData data) {
17 | super(data);
18 | }
19 |
20 | @Override
21 | public void handle(final Packet packet) {
22 | if (packet.isPosition()) {
23 | final List blocks = data.getPositionProcessor().getBoundingBox().expand(1, 1, 1).getBlocks(
24 | data.getPlayer().getWorld()
25 | );
26 |
27 | final boolean check = blocks.stream().noneMatch(block -> block.getType() != Material.AIR && !block.getType().toString().contains("WATER"))
28 | && blocks.stream().anyMatch(block -> block.getType().toString().contains("WATER"));
29 |
30 | if (check) {
31 | if ((ticks += 2) > 10) {
32 | final double prediction = data.getPositionProcessor().getLastDeltaXZ() * 0.8F;
33 | final double difference = Math.abs(data.getPositionProcessor().getLastDeltaXZ() - prediction);
34 |
35 | final boolean exempt = isExempt(ExemptType.TELEPORT, ExemptType.FLYING, ExemptType.INSIDE_VEHICLE);
36 |
37 | debug("diff=" + difference + " exempt=" + exempt + " buffer=" + buffer);
38 | if (difference > 0.04 && !exempt) {
39 | if ((buffer += 5) > 50) {
40 | fail("diff=" + difference);
41 | }
42 | } else {
43 | buffer = Math.max(buffer - 1, 0);
44 | }
45 | }
46 | } else {
47 | ticks -= ticks > 0 ? 1 : 0;
48 | }
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/movement/jesus/JesusB.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.movement.jesus;
2 |
3 | import ai.medusa.api.check.CheckInfo;
4 | import ai.medusa.anticheat.check.Check;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.exempt.type.ExemptType;
7 | import ai.medusa.anticheat.packet.Packet;
8 | import ai.medusa.anticheat.util.MathUtil;
9 | import ai.medusa.anticheat.util.PlayerUtil;
10 | import io.github.retrooper.packetevents.utils.player.ClientVersion;
11 | import org.bukkit.Material;
12 | import org.bukkit.block.Block;
13 |
14 | import java.util.List;
15 |
16 | @CheckInfo(name = "Jesus (B)", description = "Checks for irregular swimming accelerations.")
17 | public class JesusB extends Check {
18 | public JesusB(PlayerData data) {
19 | super(data);
20 | }
21 |
22 | private double lastAccel;
23 |
24 | @Override
25 | public void handle(Packet packet) {
26 | if (packet.isPosition()) {
27 | final List blocks = data.getPositionProcessor().getBlocks();
28 |
29 | final boolean touchingLily = blocks.stream().anyMatch(block -> block.getType() == Material.WATER_LILY);
30 | final boolean touchingWater = data.getPositionProcessor().isInLiquid();
31 | final boolean nearGround = data.getPositionProcessor().isOnSolidGround();
32 |
33 | final boolean exempt = isExempt(ExemptType.VELOCITY, ExemptType.FLYING, ExemptType.DEPTH_STRIDER)
34 | || touchingLily
35 | || nearGround;
36 |
37 | final double deltaY = data.getPositionProcessor().getDeltaY();
38 | final double lastDeltaY = data.getPositionProcessor().getLastDeltaY();
39 |
40 | final double accel = Math.abs(deltaY - lastDeltaY);
41 | final double lastAccel = this.lastAccel;
42 |
43 | this.lastAccel = accel;
44 |
45 | final double diff = Math.abs(accel - lastAccel);
46 |
47 | final ClientVersion clientVersion = PlayerUtil.getClientVersion(data.getPlayer());
48 |
49 | final boolean invalidAccel = diff == 0
50 | && clientVersion.isLowerThanOrEquals(ClientVersion.v_1_12_2)
51 | && Math.abs(deltaY) <= 0.05;
52 |
53 | final double deltaMax = clientVersion.isHigherThanOrEquals(ClientVersion.v_1_13) ? 0.45 : 0.101;
54 |
55 | final boolean invalidDelta = deltaY == 0 || MathUtil.isScientificNotation(deltaY) || deltaY > deltaMax;
56 |
57 | if (!exempt && touchingWater && (invalidAccel || invalidDelta)) {
58 | if (++buffer > 15) fail("dY= " + deltaY + " diff= " + diff);
59 | } else {
60 | buffer = Math.max(buffer - 0.5, 0);
61 | }
62 | }
63 | }
64 | }
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/movement/motion/MotionA.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.movement.motion;
2 |
3 | import ai.medusa.api.check.CheckInfo;
4 | import ai.medusa.anticheat.check.Check;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.exempt.type.ExemptType;
7 | import ai.medusa.anticheat.packet.Packet;
8 |
9 | /**
10 | * Created on 11/17/2020 Package ai.medusa.anticheat.check.impl.movement.motion by infinitesm
11 | */
12 |
13 | @CheckInfo(name = "Motion (A)", description = "Checks for constant vertical movement.")
14 | public final class MotionA extends Check {
15 |
16 | public MotionA(final PlayerData data) {
17 | super(data);
18 | }
19 |
20 | @Override
21 | public void handle(final Packet packet) {
22 | if (packet.isPosition()) {
23 | final boolean inAir = data.getPositionProcessor().isInAir();
24 |
25 | final double deltaY = data.getPositionProcessor().getDeltaY();
26 | final double lastDeltaY = data.getPositionProcessor().getLastDeltaY();
27 |
28 | final double acceleration = Math.abs(deltaY - lastDeltaY);
29 |
30 | final boolean exempt = isExempt(
31 | ExemptType.JOINED, ExemptType.TRAPDOOR, ExemptType.VELOCITY,
32 | ExemptType.FLYING, ExemptType.WEB, ExemptType.TELEPORT,
33 | ExemptType.LIQUID, ExemptType.SLIME, ExemptType.CLIMBABLE,
34 | ExemptType.UNDER_BLOCK, ExemptType.SLAB, ExemptType.STAIRS
35 | );
36 |
37 | if (acceleration == 0.0 && inAir && !exempt) {
38 | if ((buffer += 4) > 16) {
39 | fail(String.format("dy=%.2f", deltaY));
40 | }
41 | } else {
42 | buffer = Math.max(buffer - 1, 0);
43 | }
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/movement/motion/MotionB.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.movement.motion;
2 |
3 | import ai.medusa.anticheat.check.Check;
4 | import ai.medusa.api.check.CheckInfo;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.exempt.type.ExemptType;
7 | import ai.medusa.anticheat.packet.Packet;
8 |
9 | /**
10 | * Created on 11/17/2020 Package ai.medusa.anticheat.check.impl.movement.motion by infinitesm
11 | */
12 |
13 | @CheckInfo(name = "Motion (B)", description = "Checks for fast-fall cheats.")
14 | public final class MotionB extends Check {
15 |
16 | public MotionB(final PlayerData data) {
17 | super(data);
18 | }
19 |
20 | @Override
21 | public void handle(final Packet packet) {
22 | if (packet.isPosition()) {
23 | final double deltaY = data.getPositionProcessor().getDeltaY();
24 |
25 | debug("deltaY=" + deltaY);
26 | final boolean invalid = deltaY < -3.92 &&
27 | !isExempt(ExemptType.TELEPORT, ExemptType.FLYING);
28 |
29 | if (invalid) fail(String.format("dy=%.2f", deltaY));
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/movement/motion/MotionC.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.movement.motion;
2 |
3 | import ai.medusa.anticheat.check.Check;
4 | import ai.medusa.api.check.CheckInfo;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.exempt.type.ExemptType;
7 | import ai.medusa.anticheat.packet.Packet;
8 | import ai.medusa.anticheat.util.MathUtil;
9 | import ai.medusa.anticheat.util.PlayerUtil;
10 | import org.bukkit.potion.PotionEffectType;
11 |
12 | /**
13 | * Created on 11/17/2020 Package ai.medusa.anticheat.check.impl.movement.motion by infinitesm
14 | */
15 |
16 | @CheckInfo(name = "Motion (C)", description = "Checks for jump height/vertical movement threshold.")
17 | public final class MotionC extends Check {
18 |
19 | public MotionC(final PlayerData data) {
20 | super(data);
21 | }
22 |
23 | @Override
24 | public void handle(final Packet packet) {
25 | if (packet.isPosition()) {
26 | final double deltaY = data.getPositionProcessor().getDeltaY();
27 | final double lastPosY = data.getPositionProcessor().getLastY();
28 | final boolean onGround = data.getPositionProcessor().isOnGround();
29 |
30 | final boolean step = MathUtil.mathOnGround(deltaY) && MathUtil.mathOnGround(lastPosY);
31 |
32 | final double expectedJumpMotion = 0.42F + (double) ((float) PlayerUtil.getPotionLevel(data.getPlayer(), PotionEffectType.JUMP) * 0.1F);
33 |
34 | final double maxHighJump = 0.42F + (double) ((float) PlayerUtil.getPotionLevel(data.getPlayer(), PotionEffectType.JUMP) * 0.1F) +
35 | (data.getVelocityProcessor().getTicksSinceVelocity() < 5 ? data.getVelocityProcessor().getVelocityY() > 0 ? data.getVelocityProcessor().getVelocityY() : 0 : 0);
36 |
37 | final boolean jumped = deltaY > 0 && lastPosY % (1D/64) == 0 && !onGround && !step;
38 |
39 | final boolean exempt = isExempt(
40 | ExemptType.INSIDE_VEHICLE, ExemptType.FLYING, ExemptType.SLIME, ExemptType.UNDER_BLOCK,
41 | ExemptType.PISTON, ExemptType.LIQUID, ExemptType.NEAR_VEHICLE, ExemptType.TELEPORT,
42 | ExemptType.WEB, ExemptType.TRAPDOOR
43 | );
44 |
45 | debug("deltaY" + deltaY + " ejm=" + expectedJumpMotion + " mhj=" + maxHighJump + " step=" + step);
46 | //Mini-jump check.
47 | if (jumped && !exempt && !isExempt(ExemptType.VELOCITY)) {
48 | if (deltaY < expectedJumpMotion) {
49 | fail(String.format("ASC-L: %.2f < %.2f", deltaY, expectedJumpMotion));
50 | }
51 | }
52 |
53 | //High-jump check.
54 | if (!exempt && !step) {
55 | //Patch for mid-air step glitch.
56 | if (deltaY > (data.getPositionProcessor().isOnGround() ? 0.6 : maxHighJump)) {
57 | fail(String.format(
58 | "ASC-H: %.2f > %.2f, onGround=%b",
59 | deltaY, expectedJumpMotion, data.getPositionProcessor().isOnGround()
60 | ));
61 | }
62 | }
63 |
64 | //Generic step check.
65 | if (step && !isExempt(ExemptType.TELEPORT)) {
66 | if (deltaY > 0.6F) {
67 | fail(String.format("ASC-S: %.2f > 0.6", deltaY));
68 | }
69 | }
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/movement/motion/MotionD.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.movement.motion;
2 |
3 | import ai.medusa.anticheat.check.Check;
4 | import ai.medusa.api.check.CheckInfo;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.exempt.type.ExemptType;
7 | import ai.medusa.anticheat.packet.Packet;
8 | import org.bukkit.util.Vector;
9 |
10 | /**
11 | * Created on 11/17/2020 Package ai.medusa.anticheat.check.impl.movement.motion by infinitesm
12 | */
13 |
14 | @CheckInfo(name = "Motion (D)", description = "Checks for sprint direction.")
15 | public final class MotionD extends Check {
16 |
17 | private int teleportTicks;
18 | private int offGroundTicks;
19 | private final Vector direction = new Vector(0, 0, 0);
20 |
21 | public MotionD(final PlayerData data) {
22 | super(data);
23 | }
24 |
25 | @Override
26 | public void handle(final Packet packet) {
27 | if (packet.isFlying()) {
28 | if (data.getActionProcessor().isSprinting() &&
29 | data.getVelocityProcessor().getTicksSinceVelocity() > 15 &&
30 | ++teleportTicks > 10 && !data.getPlayer().isFlying()) {
31 | final double deltaX = data.getPositionProcessor().getX() - data.getPositionProcessor().getLastX();
32 | final double deltaZ = data.getPositionProcessor().getZ() - data.getPositionProcessor().getLastZ();
33 |
34 | final double directionX = -Math.sin(data.getPlayer().getEyeLocation().getYaw() * 3.1415927F / 180.0F) * (float) 1 * 0.5F;
35 | final double directionZ = Math.cos(data.getPlayer().getEyeLocation().getYaw() * 3.1415927F / 180.0F) * (float) 1 * 0.5F;
36 |
37 | final Vector positionDifference = new Vector(deltaX, 0, deltaZ);
38 |
39 | if (data.getPlayer().isOnGround()) {
40 | offGroundTicks = 0;
41 | direction.setX(directionX);
42 | direction.setY(0);
43 | direction.setZ(directionZ);
44 | } else {
45 | ++offGroundTicks;
46 | }
47 |
48 | final double angle = Math.toDegrees(positionDifference.angle(direction));
49 |
50 | final boolean invalid = !data.getPositionProcessor().isInLiquid()
51 | && angle > 85
52 | && data.getPositionProcessor().getDeltaXZ() > 0.25
53 | && offGroundTicks < 8
54 | && !isExempt(ExemptType.TELEPORT, ExemptType.VELOCITY);
55 |
56 | debug("angle=" + angle + " dxz=" + data.getPositionProcessor().getDeltaXZ() + " buffer=" + buffer);
57 | if (invalid) {
58 | if (++buffer >= 8) {
59 | fail(String.format("angle=%.2f, buffer=%.2f", angle, buffer));
60 | }
61 | } else {
62 | buffer = Math.max(buffer - 0.5, 0);
63 | }
64 | }
65 | } else if (packet.isTeleport()) {
66 | teleportTicks = 0;
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/movement/motion/MotionE.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.movement.motion;
2 |
3 | import ai.medusa.anticheat.check.Check;
4 | import ai.medusa.api.check.CheckInfo;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.exempt.type.ExemptType;
7 | import ai.medusa.anticheat.packet.Packet;
8 |
9 | /**
10 | * Created on 11/17/2020 Package ai.medusa.anticheat.check.impl.movement.speed by infinitesm
11 | */
12 | @CheckInfo(name = "Motion (E)", description = "Checks for switching direction mid-air.")
13 | public final class MotionE extends Check {
14 |
15 | public MotionE(final PlayerData data) {
16 | super(data);
17 | }
18 |
19 | @Override
20 | public void handle(final Packet packet) {
21 | if (packet.isPosition()) {
22 | final double deltaX = data.getPositionProcessor().getDeltaX();
23 | final double lastDeltaX = data.getPositionProcessor().getLastDeltaX();
24 |
25 | final double deltaZ = data.getPositionProcessor().getDeltaZ();
26 | final double lastDeltaZ = data.getPositionProcessor().getLastDeltaZ();
27 |
28 | final double absDeltaX = Math.abs(deltaX);
29 | final double absDeltaZ = Math.abs(deltaZ);
30 |
31 | final double absLastDeltaX = Math.abs(lastDeltaX);
32 | final double absLastDeltaZ = Math.abs(lastDeltaZ);
33 |
34 | if (data.getPositionProcessor().getAirTicks() > 2 && !isExempt(ExemptType.VELOCITY, ExemptType.NEAR_VEHICLE, ExemptType.TELEPORT, ExemptType.FLYING)) {
35 | final boolean xSwitched = (deltaX > 0 && lastDeltaX < 0) || (deltaX < 0 && lastDeltaX > 0);
36 | final boolean zSwitched = (deltaZ > 0 && lastDeltaZ < 0) || (deltaZ < 0 && lastDeltaZ > 0);
37 |
38 | if (xSwitched) {
39 | if (Math.abs(absDeltaX - absLastDeltaX) > 0.05) {
40 | if (++buffer > 1.25) {
41 | fail();
42 | }
43 | }
44 | } else {
45 | buffer = Math.max(buffer - 0.05, 0);
46 | }
47 | if (zSwitched) {
48 | if (Math.abs(absDeltaZ - absLastDeltaZ) > 0.05) {
49 | if (++buffer > 1.25) {
50 | fail();
51 | }
52 | }
53 | } else {
54 | buffer = Math.max(buffer - 0.05, 0);
55 | }
56 | }
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/movement/noslow/NoSlowA.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.movement.noslow;
2 |
3 | import ai.medusa.api.check.CheckInfo;
4 | import ai.medusa.anticheat.check.Check;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.packet.Packet;
7 | import io.github.retrooper.packetevents.PacketEvents;
8 | import io.github.retrooper.packetevents.packetwrappers.play.out.helditemslot.WrappedPacketOutHeldItemSlot;
9 |
10 | @CheckInfo(name = "NoSlow (A)", description = "Checks for NoSlow modules.", experimental = true)
11 | public final class NoSlowA extends Check {
12 |
13 | public NoSlowA(final PlayerData data) {
14 | super(data);
15 | }
16 |
17 | @Override
18 | public void handle(final Packet packet) {
19 | if (packet.isPosition()) {
20 | if (data.getActionProcessor().isSprinting() && data.getPlayer().isBlocking()) {
21 | debug("buffer=" + buffer);
22 | if (++buffer > 10) {
23 | fail("buffer=" + buffer);
24 | final int slot = data.getActionProcessor().getHeldItemSlot() == 8 ? 1 : 8;
25 | final WrappedPacketOutHeldItemSlot wrapper = new WrappedPacketOutHeldItemSlot(slot);
26 | PacketEvents.get().getPlayerUtils().sendPacket(data.getPlayer(), wrapper);
27 | buffer /= 2;
28 | }
29 | } else {
30 | buffer -= buffer > 0 ? 0.25 : 0;
31 | }
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/movement/phase/PhaseA.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.movement.phase;
2 |
3 | import ai.medusa.api.check.CheckInfo;
4 | import ai.medusa.anticheat.check.Check;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.data.processor.PositionProcessor;
7 | import ai.medusa.anticheat.packet.Packet;
8 | import ai.medusa.anticheat.util.type.BoundingBox;
9 | import org.bukkit.block.Block;
10 |
11 | import java.util.List;
12 |
13 | @CheckInfo(name = "Phase (A)", description = "Checks if the player moves through unpassable blocks", experimental = true)
14 | public class PhaseA extends Check {
15 |
16 | public PhaseA(final PlayerData data) {
17 | super(data);
18 | }
19 |
20 | @Override
21 | public void handle(final Packet packet) {
22 | if (packet.isPosition()) {
23 | final PositionProcessor position = data.getPositionProcessor();
24 |
25 | final BoundingBox toBB = new BoundingBox(
26 | position.getX() - 0.3, position.getX() + 0.3,
27 | position.getY(), position.getY() + 1.705,
28 | position.getZ() - 0.3, position.getZ() + 0.3
29 | );
30 |
31 | final BoundingBox fromBB = new BoundingBox(
32 | position.getLastX() - 0.3, position.getLastX() + 0.3,
33 | position.getLastY(), position.getLastY() + 0.3,
34 | position.getLastZ() - 0.3, position.getLastZ() + 0.3
35 | );
36 |
37 | final BoundingBox unionBox = toBB.union(fromBB);
38 |
39 | if (unionBox.getSize() > 10) return;
40 |
41 | final List collidedBlocks = unionBox.getBlocks(data.getPlayer().getWorld());
42 |
43 | final int size = (int) collidedBlocks.stream().filter(block -> block.getType().isSolid()).count();
44 |
45 | if (size >= 4) {
46 | fail("collided=" + size);
47 | }
48 |
49 | debug("collided=" + size);
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/movement/speed/SpeedA.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.movement.speed;
2 |
3 | import ai.medusa.anticheat.check.Check;
4 | import ai.medusa.api.check.CheckInfo;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.packet.Packet;
7 |
8 | /**
9 | * Created on 11/17/2020 Package ai.medusa.anticheat.check.impl.movement.speed by infinitesm
10 | */
11 |
12 | @CheckInfo(name = "Speed (A)", description = "Checks for horizontal friction.")
13 | public final class SpeedA extends Check {
14 |
15 | public SpeedA(final PlayerData data) {
16 | super(data);
17 | }
18 |
19 | @Override
20 | public void handle(final Packet packet) {
21 | if (packet.isPosition()) {
22 | final double deltaXZ = data.getPositionProcessor().getDeltaXZ();
23 | final double lastDeltaXZ = data.getPositionProcessor().getLastDeltaXZ();
24 |
25 | final double prediction = lastDeltaXZ * 0.91F + (data.getActionProcessor().isSprinting() ? 0.026 : 0.02);
26 | final double difference = deltaXZ - prediction;
27 |
28 | final boolean invalid = difference > 1e-12 &&
29 | data.getPositionProcessor().getAirTicks() > 2 &&
30 | !data.getPositionProcessor().isFlying() &&
31 | !data.getPositionProcessor().isNearVehicle();
32 |
33 | debug("diff=" + difference);
34 | if (invalid) {
35 | if ((buffer += buffer < 100 ? 5 : 0) > 40) {
36 | fail(String.format("diff=%.4f", difference));
37 | }
38 | } else {
39 | buffer = Math.max(buffer - 1, 0);
40 | }
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/movement/speed/SpeedC.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.movement.speed;
2 |
3 | import ai.medusa.api.check.CheckInfo;
4 | import ai.medusa.anticheat.check.Check;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.exempt.type.ExemptType;
7 | import ai.medusa.anticheat.packet.Packet;
8 | import ai.medusa.anticheat.util.PlayerUtil;
9 | import org.bukkit.potion.PotionEffectType;
10 |
11 | @CheckInfo(name = "Speed (C)", description = "Basic verbose speed check.", experimental = true)
12 | public final class SpeedC extends Check {
13 |
14 | public SpeedC(final PlayerData data) {
15 | super(data);
16 | }
17 |
18 | @Override
19 | public void handle(final Packet packet) {
20 | if (packet.isPosition()) {
21 | final double deltaXZ = data.getPositionProcessor().getDeltaXZ();
22 |
23 | final double maxSpeed = getSpeed(0.4);
24 |
25 | final boolean exempt = isExempt(
26 | ExemptType.TELEPORT, ExemptType.JOINED, ExemptType.PISTON, ExemptType.VELOCITY,
27 | ExemptType.INSIDE_VEHICLE, ExemptType.FLYING, ExemptType.SLIME, ExemptType.UNDER_BLOCK,
28 | ExemptType.ICE
29 | );
30 |
31 | debug("dxz-ms" + (deltaXZ-maxSpeed) + " buffer=" + buffer);
32 | if (deltaXZ > maxSpeed && !exempt) {
33 | buffer += buffer < 15 ? 1 : 0;
34 | if (buffer > 10) {
35 | fail("deltaXZ=" + deltaXZ + " max=" + maxSpeed);
36 | buffer /= 2;
37 | }
38 | } else {
39 | buffer -= buffer > 0 ? 0.25 : 0;
40 | }
41 | }
42 | }
43 |
44 | private double getSpeed(double movement) {
45 | if (PlayerUtil.getPotionLevel(data.getPlayer(), PotionEffectType.SPEED) > 0) {
46 | movement *= 1.0D + 0.2D * (double)(PlayerUtil.getPotionLevel(data.getPlayer(), PotionEffectType.SPEED));
47 | }
48 | return movement;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/player/hand/HandA.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.player.hand;
2 |
3 | import ai.medusa.anticheat.check.Check;
4 | import ai.medusa.api.check.CheckInfo;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.packet.Packet;
7 | import io.github.retrooper.packetevents.packetwrappers.play.in.blockplace.WrappedPacketInBlockPlace;
8 | import io.github.retrooper.packetevents.utils.player.Direction;
9 | import org.bukkit.Location;
10 |
11 | /**
12 | * Created on 1/18/2021 Package ai.medusa.anticheat.check.impl.player.hand by infinitesm
13 | */
14 |
15 | @CheckInfo(name = "Hand (A)", experimental = true, description = "Checks for face occlusion when placing blocks.")
16 | public final class HandA extends Check {
17 |
18 | public HandA(final PlayerData data) {
19 | super(data);
20 | }
21 |
22 | @Override
23 | public void handle(final Packet packet) {
24 | if (packet.isBlockPlace()) {
25 | final WrappedPacketInBlockPlace wrapper = new WrappedPacketInBlockPlace(packet.getRawPacket());
26 |
27 | final Direction direction = wrapper.getDirection();
28 |
29 | final Location blockLocation = new Location(
30 | data.getPlayer().getWorld(),
31 | wrapper.getBlockPosition().getX(),
32 | wrapper.getBlockPosition().getY(),
33 | wrapper.getBlockPosition().getZ()
34 | );
35 |
36 | final Location eyeLocation = data.getPlayer().getEyeLocation();
37 | final Location blockAgainstLocation = getBlockAgainst(direction, blockLocation);
38 |
39 | final boolean validInteraction = interactedCorrectly(blockAgainstLocation, eyeLocation, direction);
40 |
41 | if (!validInteraction) {
42 | assert direction != null;
43 | fail("face=" + direction.getFaceValue());
44 | }
45 | }
46 | }
47 |
48 | private Location getBlockAgainst(final Direction direction, final Location blockLocation) {
49 | if (Direction.UP.equals(direction)) {
50 | return blockLocation.clone().add(0, -1, 0);
51 | } else if (Direction.DOWN.equals(direction)) {
52 | return blockLocation.clone().add(0, 1, 0);
53 | } else if (Direction.EAST.equals(direction) || Direction.SOUTH.equals(direction)) {
54 | return blockLocation;
55 | } else if (Direction.WEST.equals(direction)) {
56 | return blockLocation.clone().add(1, 0, 0);
57 | } else if (Direction.NORTH.equals(direction)) {
58 | return blockLocation.clone().add(0, 0, 1);
59 | }
60 | return null;
61 | }
62 | private boolean interactedCorrectly(Location block, Location player, Direction face) {
63 | if (Direction.UP.equals(face)) {
64 | return player.getY() > block.getY();
65 | } else if (Direction.DOWN.equals(face)) {
66 | return player.getY() < block.getY();
67 | } else if (Direction.WEST.equals(face)) {
68 | return player.getX() < block.getX();
69 | } else if (Direction.EAST.equals(face)) {
70 | return player.getX() > block.getX();
71 | } else if (Direction.NORTH.equals(face)) {
72 | return player.getZ() < block.getZ();
73 | } else if (Direction.SOUTH.equals(face)) {
74 | return player.getZ() > block.getZ();
75 | }
76 | return true;
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/player/inventory/InventoryA.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.player.inventory;
2 |
3 | import ai.medusa.api.check.CheckInfo;
4 | import ai.medusa.anticheat.check.Check;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.packet.Packet;
7 |
8 | @CheckInfo(name = "Inventory (A)", description = "Checks for sprinting in inventory.", experimental = true)
9 | public final class InventoryA extends Check {
10 |
11 | public InventoryA(final PlayerData data) {
12 | super(data);
13 | }
14 |
15 | @Override
16 | public void handle(final Packet packet) {
17 | if (packet.isPosition()) {
18 | if (data.getActionProcessor().isInventory()) {
19 | if (data.getActionProcessor().isSprinting()) {
20 | if (++buffer > 5) {
21 | fail();
22 | if (buffer >= 20) {
23 | data.getPlayer().closeInventory();
24 | buffer = 0;
25 | data.getActionProcessor().setInventory(false);
26 | }
27 | }
28 | } else {
29 | buffer -= buffer > 0 ? 1 : 0;
30 | }
31 | }
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/player/inventory/InventoryB.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.player.inventory;
2 |
3 | import ai.medusa.api.check.CheckInfo;
4 | import ai.medusa.anticheat.check.Check;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.packet.Packet;
7 | import io.github.retrooper.packetevents.packetwrappers.play.in.windowclick.WrappedPacketInWindowClick;
8 |
9 | @CheckInfo(name = "Inventory (B)", description = "Checks for moving inventory items too quickly.", experimental = true)
10 | public final class InventoryB extends Check {
11 |
12 | private int movements;
13 |
14 | public InventoryB(final PlayerData data) {
15 | super(data);
16 | }
17 |
18 | @Override
19 | public void handle(final Packet packet) {
20 | if (packet.isWindowClick()) {
21 | final WrappedPacketInWindowClick wrapper = new WrappedPacketInWindowClick(packet.getRawPacket());
22 |
23 | if (wrapper.getMode() == 1) {
24 | if (movements <= 1) {
25 | if (++buffer > 5) {
26 | fail("ticks=" + movements + " mode=1");
27 | }
28 | } else {
29 | buffer -= buffer > 0 ? 0.5 : 0;
30 | }
31 | } else if (wrapper.getMode() == 4) {
32 | if (movements == 0) {
33 | if (++buffer > 5) {
34 | fail("ticks=" + movements + " mode=4");
35 | }
36 | } else {
37 | buffer -= buffer > 0 ? 0.5 : 0;
38 | }
39 | }
40 |
41 | movements = 0;
42 | } else if (packet.isFlying()) {
43 | ++movements;
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/player/protocol/ProtocolA.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.player.protocol;
2 |
3 | import ai.medusa.anticheat.check.Check;
4 | import ai.medusa.api.check.CheckInfo;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.packet.Packet;
7 |
8 | /**
9 | * Created on 11/14/2020 Package ai.medusa.anticheat.check.impl.player.Protocol by infinitesm
10 | */
11 |
12 | @CheckInfo(name = "Protocol (A)", description = "Checks for invalid pitch rotation.")
13 | public final class ProtocolA extends Check {
14 |
15 | public ProtocolA(final PlayerData data) {
16 | super(data);
17 | }
18 |
19 | @Override
20 | public void handle(final Packet packet) {
21 | if (packet.isRotation()) {
22 | final float pitch = data.getRotationProcessor().getPitch();
23 |
24 | if (Math.abs(pitch) > 90) fail(String.format("pitch=%.2f", pitch));
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/player/protocol/ProtocolB.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.player.protocol;
2 |
3 | import ai.medusa.anticheat.check.Check;
4 | import ai.medusa.api.check.CheckInfo;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.packet.Packet;
7 | import io.github.retrooper.packetevents.packetwrappers.play.in.abilities.WrappedPacketInAbilities;
8 |
9 | /**
10 | * Created on 11/14/2020 Package ai.medusa.anticheat.check.impl.player.Protocol by infinitesm
11 | */
12 |
13 |
14 | @CheckInfo(name = "Protocol (B)", description = "Checks for spoofed abilities packets.")
15 | public final class ProtocolB extends Check {
16 |
17 | public ProtocolB(final PlayerData data) {
18 | super(data);
19 | }
20 |
21 | @Override
22 | public void handle(final Packet packet) {
23 | if (packet.isAbilities()) {
24 | final WrappedPacketInAbilities wrapper = new WrappedPacketInAbilities(packet.getRawPacket());
25 |
26 | final boolean invalid = wrapper.isFlightAllowed() && !data.getPlayer().getAllowFlight();
27 |
28 | if (invalid) {
29 | fail();
30 | }
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/player/protocol/ProtocolC.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.player.protocol;
2 |
3 | import ai.medusa.anticheat.check.Check;
4 | import ai.medusa.api.check.CheckInfo;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.packet.Packet;
7 | import io.github.retrooper.packetevents.packetwrappers.play.in.flying.WrappedPacketInFlying;
8 |
9 | /**
10 | * Created on 11/14/2020 Package ai.medusa.anticheat.check.impl.player.Protocol by infinitesm
11 | */
12 |
13 |
14 | @CheckInfo(name = "Protocol (C)", description = "Checks for flying packet sequence.")
15 | public final class ProtocolC extends Check {
16 |
17 | private int streak;
18 |
19 | public ProtocolC(final PlayerData data) {
20 | super(data);
21 | }
22 |
23 | @Override
24 | public void handle(final Packet packet) {
25 | if (packet.isFlying()) {
26 | final WrappedPacketInFlying wrapper = new WrappedPacketInFlying(packet.getRawPacket());
27 |
28 | if (wrapper.isPosition() || data.getPlayer().isInsideVehicle()) {
29 | streak = 0;
30 | return;
31 | }
32 |
33 | if (++streak > 20) {
34 | fail("streak=" + streak);
35 | }
36 | } else if (packet.isSteerVehicle()) {
37 | streak = 0;
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/player/protocol/ProtocolD.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.player.protocol;
2 |
3 | import ai.medusa.anticheat.check.Check;
4 | import ai.medusa.api.check.CheckInfo;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.packet.Packet;
7 | import io.github.retrooper.packetevents.packetwrappers.play.in.steervehicle.WrappedPacketInSteerVehicle;
8 |
9 | /**
10 | * Created on 11/14/2020 Package ai.medusa.anticheat.check.impl.player.Protocol by infinitesm
11 | */
12 |
13 |
14 | @CheckInfo(name = "Protocol (D)", description = "Checks for a common exploit in disabler modules.")
15 | public final class ProtocolD extends Check {
16 |
17 | public ProtocolD(final PlayerData data) {
18 | super(data);
19 | }
20 |
21 |
22 | @Override
23 | public void handle(final Packet packet) {
24 | if (packet.isSteerVehicle()) {
25 | final WrappedPacketInSteerVehicle wrapper = new WrappedPacketInSteerVehicle(packet.getRawPacket());
26 |
27 | final float forwardValue = Math.abs(wrapper.getForwardValue());
28 | final float sideValue = Math.abs(wrapper.getSideValue());
29 |
30 | final boolean invalid = forwardValue > .98F || sideValue > .98F;
31 |
32 | if (invalid) {
33 | fail();
34 | }
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/player/protocol/ProtocolE.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.player.protocol;
2 |
3 | import ai.medusa.anticheat.check.Check;
4 | import ai.medusa.api.check.CheckInfo;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.packet.Packet;
7 | import io.github.retrooper.packetevents.packetwrappers.play.in.helditemslot.WrappedPacketInHeldItemSlot;
8 |
9 | /**
10 | * Created on 11/14/2020 Package ai.medusa.anticheat.check.impl.player.Protocol by infinitesm
11 | */
12 |
13 |
14 | @CheckInfo(name = "Protocol (E)", description = "Checks for flaws in scaffold/auto-tool hacks.")
15 | public final class ProtocolE extends Check {
16 |
17 | private int lastSlot = -1;
18 |
19 | public ProtocolE(final PlayerData data) {
20 | super(data);
21 | }
22 |
23 | @Override
24 | public void handle(final Packet packet) {
25 | if (packet.isHeldItemSlot()) {
26 | final WrappedPacketInHeldItemSlot wrapper = new WrappedPacketInHeldItemSlot(packet.getRawPacket());
27 |
28 | final int slot = wrapper.getCurrentSelectedSlot();
29 |
30 | if (slot == lastSlot) {
31 | fail();
32 | }
33 |
34 | lastSlot = slot;
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/player/protocol/ProtocolF.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.player.protocol;
2 |
3 | import ai.medusa.anticheat.check.Check;
4 | import ai.medusa.api.check.CheckInfo;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.packet.Packet;
7 | import io.github.retrooper.packetevents.packetwrappers.play.in.steervehicle.WrappedPacketInSteerVehicle;
8 |
9 | /**
10 | * Created on 11/14/2020 Package ai.medusa.anticheat.check.impl.player.Protocol by infinitesm
11 | */
12 |
13 |
14 | @CheckInfo(name = "Protocol (F)", description = "Checks for a common exploit in disabler modules.")
15 | public final class ProtocolF extends Check {
16 |
17 | public ProtocolF(final PlayerData data) {
18 | super(data);
19 | }
20 |
21 | @Override
22 | public void handle(final Packet packet) {
23 | if (packet.isSteerVehicle()) {
24 | final WrappedPacketInSteerVehicle wrapper = new WrappedPacketInSteerVehicle(packet.getRawPacket());
25 |
26 | final boolean unmount = wrapper.isDismount();
27 |
28 | final boolean invalid = data.getPlayer().getVehicle() == null && !unmount;
29 |
30 | if (invalid) {
31 | if (++buffer > 8) {
32 | fail("buffer=" + buffer);
33 | buffer /= 2;
34 | }
35 | } else {
36 | buffer = 0;
37 | }
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/player/protocol/ProtocolG.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.player.protocol;
2 |
3 | import ai.medusa.anticheat.check.Check;
4 | import ai.medusa.api.check.CheckInfo;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.packet.Packet;
7 |
8 | /**
9 | * Created on 11/10/2020 Package ai.medusa.anticheat.check.impl.player.scaffold by infinitesm
10 | */
11 |
12 | @CheckInfo(name = "Protocol (G)", description = "Checks for packet order.")
13 | public final class ProtocolG extends Check {
14 |
15 | private long lastBlockPlace;
16 | private boolean placedBlock;
17 |
18 | public ProtocolG(final PlayerData data) {
19 | super(data);
20 | }
21 |
22 | @Override
23 | public void handle(final Packet packet) {
24 | if (packet.isBlockPlace()) {
25 | lastBlockPlace = now();
26 | placedBlock = true;
27 | } else if (packet.isFlying()) {
28 | if (placedBlock) {
29 | final long delay = now() - lastBlockPlace;
30 | final boolean invalid = !data.getActionProcessor().isLagging() && delay > 25;
31 |
32 | if (invalid) {
33 | if (++buffer > 5) {
34 | fail("delay=" + delay + " buffer=" + buffer);
35 | }
36 | } else {
37 | buffer = Math.max(buffer - 2, 0);
38 | }
39 | }
40 | placedBlock = false;
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/player/protocol/ProtocolH.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.player.protocol;
2 |
3 | import ai.medusa.anticheat.check.Check;
4 | import ai.medusa.api.check.CheckInfo;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.packet.Packet;
7 | import io.github.retrooper.packetevents.packetwrappers.play.in.useentity.WrappedPacketInUseEntity;
8 |
9 | @CheckInfo(name = "Protocol (H)", description = "Checks for tick stack order in block hitting.")
10 | public final class ProtocolH extends Check {
11 |
12 | public ProtocolH(final PlayerData data) {
13 | super(data);
14 | }
15 |
16 | @Override
17 | public void handle(final Packet packet) {
18 | if (packet.isUseEntity()) {
19 | final WrappedPacketInUseEntity wrapper = new WrappedPacketInUseEntity(packet.getRawPacket());
20 |
21 | if (wrapper.getAction() == WrappedPacketInUseEntity.EntityUseAction.ATTACK) {
22 | final boolean invalid = data.getActionProcessor().isPlacing();
23 |
24 | if (invalid) {
25 | fail();
26 | }
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/player/protocol/ProtocolI.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.player.protocol;
2 |
3 | import ai.medusa.anticheat.check.Check;
4 | import ai.medusa.api.check.CheckInfo;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.packet.Packet;
7 | import io.github.retrooper.packetevents.packetwrappers.play.in.useentity.WrappedPacketInUseEntity;
8 |
9 | @CheckInfo(name = "Protocol (I)", description = "Validates block dig packets.")
10 | public final class ProtocolI extends Check {
11 |
12 | public ProtocolI(final PlayerData data) {
13 | super(data);
14 | }
15 |
16 | @Override
17 | public void handle(final Packet packet) {
18 | if (packet.isUseEntity()) {
19 | final WrappedPacketInUseEntity wrapper = new WrappedPacketInUseEntity(packet.getRawPacket());
20 |
21 | if (wrapper.getAction() == WrappedPacketInUseEntity.EntityUseAction.ATTACK) {
22 | final boolean invalid = data.getActionProcessor().isBlocking();
23 |
24 | if (invalid) {
25 | fail();
26 | }
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/player/protocol/ProtocolJ.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.player.protocol;
2 |
3 | import ai.medusa.anticheat.check.Check;
4 | import ai.medusa.api.check.CheckInfo;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.packet.Packet;
7 | import io.github.retrooper.packetevents.packetwrappers.play.in.useentity.WrappedPacketInUseEntity;
8 |
9 | @CheckInfo(name = "Protocol (J)", description = "Checks for attacking and digging.")
10 | public final class ProtocolJ extends Check {
11 |
12 | public ProtocolJ(final PlayerData data) {
13 | super(data);
14 | }
15 |
16 | @Override
17 | public void handle(final Packet packet) {
18 | if (packet.isUseEntity()) {
19 | final WrappedPacketInUseEntity wrapper = new WrappedPacketInUseEntity(packet.getRawPacket());
20 |
21 | if (wrapper.getAction() == WrappedPacketInUseEntity.EntityUseAction.ATTACK) {
22 | final boolean sword = data.getPlayer().getItemInHand().getType().toString().contains("SWORD");
23 | final boolean invalid = data.getActionProcessor().isSendingDig();
24 |
25 | if (invalid && sword) {
26 | if (++buffer > 5) {
27 | fail();
28 | }
29 | } else {
30 | buffer = Math.max(buffer - 1, 0);
31 | }
32 | }
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/player/scaffold/ScaffoldA.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.player.scaffold;
2 |
3 | import ai.medusa.anticheat.check.Check;
4 | import ai.medusa.api.check.CheckInfo;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.packet.Packet;
7 |
8 | /**
9 | * Created on 11/10/2020 Package ai.medusa.anticheat.check.impl.player.scaffold by infinitesm
10 | */
11 |
12 | @CheckInfo(name = "Scaffold (A)", description = "Checks for movement/rotations that are related to scaffold modules.")
13 | public final class ScaffoldA extends Check {
14 |
15 | private boolean placedBlock;
16 |
17 | public ScaffoldA(final PlayerData data) {
18 | super(data);
19 | }
20 |
21 | @Override
22 | public void handle(final Packet packet) {
23 | if (packet.isFlying()) {
24 | if (placedBlock && isBridging()) {
25 | final double deltaXZ = data.getPositionProcessor().getDeltaXZ();
26 | final double lastDeltaXZ = data.getPositionProcessor().getLastDeltaXZ();
27 |
28 | final double accel = Math.abs(deltaXZ - lastDeltaXZ);
29 |
30 | final float deltaYaw = data.getRotationProcessor().getDeltaYaw() % 360F;
31 | final float deltaPitch = data.getRotationProcessor().getDeltaPitch();
32 |
33 | final boolean invalid = deltaYaw > 75F && deltaPitch > 15F && accel < 0.15;
34 |
35 | if (invalid) {
36 | if (++buffer > 3) {
37 | fail(String.format("accel=%.2f, deltaYaw=%.2f, deltaPitch=%.2f", accel, deltaYaw, deltaPitch));
38 | }
39 | } else {
40 | buffer = Math.max(buffer - 0.5, 0);
41 | }
42 | }
43 | placedBlock = false;
44 | } else if (packet.isBlockPlace()) {
45 | if (data.getPlayer().getItemInHand().getType().isBlock()) {
46 | placedBlock = true;
47 | }
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/player/scaffold/ScaffoldB.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.player.scaffold;
2 |
3 | import ai.medusa.api.check.CheckInfo;
4 | import ai.medusa.anticheat.check.Check;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.packet.Packet;
7 | import ai.medusa.anticheat.util.PlayerUtil;
8 | import io.github.retrooper.packetevents.packetwrappers.play.in.blockplace.WrappedPacketInBlockPlace;
9 | import org.bukkit.potion.PotionEffectType;
10 |
11 | @CheckInfo(name = "Scaffold (B)", description = "Checks for tower hacks.", experimental = true)
12 | public final class ScaffoldB extends Check {
13 |
14 | private int movements;
15 | private int lastX, lastY, lastZ;
16 | public ScaffoldB(final PlayerData data) {
17 | super(data);
18 | }
19 |
20 | @Override
21 | public void handle(final Packet packet) {
22 | if (packet.isBlockPlace()) {
23 | final WrappedPacketInBlockPlace wrapper = new WrappedPacketInBlockPlace(packet.getRawPacket());
24 |
25 | if (PlayerUtil.getPotionLevel(data.getPlayer(), PotionEffectType.JUMP) > 0) return;
26 | if (data.getPositionProcessor().getDeltaY() <= 0) return;
27 |
28 | if (!(wrapper.getBlockPosition().getX() == 1 && wrapper.getBlockPosition().getY() == 1 && wrapper.getBlockPosition().getZ() != 1)) {
29 | if (data.getPlayer().getItemInHand().getType().isBlock()) {
30 | if (lastX == wrapper.getBlockPosition().getX() && wrapper.getBlockPosition().getY() > lastY && lastZ == wrapper.getBlockPosition().getZ()) {
31 | if (movements < 7) {
32 | if (++buffer > 2) {
33 | fail("ticks=" + movements);
34 | }
35 | } else {
36 | buffer = 0;
37 | }
38 | movements = 0;
39 | }
40 | lastX = wrapper.getBlockPosition().getX();
41 | lastY = wrapper.getBlockPosition().getY();
42 | lastZ = wrapper.getBlockPosition().getZ();
43 | }
44 | }
45 | } else if (packet.isFlying()) {
46 | ++movements;
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/player/scaffold/ScaffoldC.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.player.scaffold;
2 |
3 | import ai.medusa.api.check.CheckInfo;
4 | import ai.medusa.anticheat.check.Check;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.packet.Packet;
7 | import io.github.retrooper.packetevents.packetwrappers.play.in.blockplace.WrappedPacketInBlockPlace;
8 | import io.github.retrooper.packetevents.utils.player.Direction;
9 |
10 | @CheckInfo(name = "Scaffold (C)", description = "Checks for downwards scaffold.", experimental = true)
11 | public final class ScaffoldC extends Check {
12 |
13 | public ScaffoldC(final PlayerData data) {
14 | super(data);
15 | }
16 |
17 | @Override
18 | public void handle(final Packet packet) {
19 | if (packet.isBlockPlace()) {
20 | final WrappedPacketInBlockPlace wrapper = new WrappedPacketInBlockPlace(packet.getRawPacket());
21 |
22 | if (!(wrapper.getBlockPosition().getX() == 1 && wrapper.getBlockPosition().getY() == 1 && wrapper.getBlockPosition().getZ() == 1)) {
23 | if (data.getPlayer().getItemInHand().getType().isBlock()) {
24 | if (wrapper.getDirection() == Direction.DOWN) {
25 | if (wrapper.getBlockPosition().getY() < data.getPositionProcessor().getY()) fail();
26 | }
27 | }
28 | }
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/player/scaffold/ScaffoldD.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.player.scaffold;
2 |
3 | import ai.medusa.api.check.CheckInfo;
4 | import ai.medusa.anticheat.check.Check;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.packet.Packet;
7 |
8 | @CheckInfo(name = "Scaffold (D)", description = "Checks for SafeWalk scaffold.", experimental = true)
9 | public final class ScaffoldD extends Check {
10 |
11 | private int placedTicks;
12 |
13 | private double lastLastAccel, lastAccel;
14 |
15 | public ScaffoldD(final PlayerData data) {
16 | super(data);
17 | }
18 |
19 | @Override
20 | public void handle(final Packet packet) {
21 | if (packet.isPosition()) {
22 | if (++placedTicks < 5 && isBridging() && !data.getActionProcessor().isSneaking()) {
23 | final double accel = data.getPositionProcessor().getDeltaXZ() - data.getPositionProcessor().getLastDeltaXZ();
24 |
25 | debug("accel=" + accel);
26 | if (accel < 0.0001 && lastAccel > 0.05 && lastLastAccel < 0.0001) {
27 | if (++buffer > 3) {
28 | fail();
29 | buffer = 0;
30 | }
31 | }
32 |
33 | lastLastAccel = lastAccel;
34 | lastAccel = accel;
35 | }
36 | } else if (packet.isBlockPlace()) {
37 | if (data.getPlayer().getItemInHand().getType().isBlock()) placedTicks = 0;
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/player/timer/TimerA.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.player.timer;
2 |
3 | import ai.medusa.anticheat.check.Check;
4 | import ai.medusa.api.check.CheckInfo;
5 | import ai.medusa.anticheat.config.ConfigValue;
6 | import ai.medusa.anticheat.data.PlayerData;
7 | import ai.medusa.anticheat.exempt.type.ExemptType;
8 | import ai.medusa.anticheat.packet.Packet;
9 | import ai.medusa.anticheat.util.MathUtil;
10 | import ai.medusa.anticheat.util.type.EvictingList;
11 |
12 | /**
13 | * Created on 11/13/2020 Package ai.medusa.anticheat.check.impl.player.timer by infinitesm
14 | */
15 |
16 | @CheckInfo(name = "Timer (A)", description = "Checks for game speed which is too fast.")
17 | public final class TimerA extends Check {
18 |
19 | private static final ConfigValue maxTimerSpeed = new ConfigValue(ConfigValue.ValueType.DOUBLE, "max-timer-speed");
20 | private final EvictingList samples = new EvictingList<>(50);
21 | private long lastFlyingTime;
22 |
23 | public TimerA(final PlayerData data) {
24 | super(data);
25 | }
26 |
27 | @Override
28 | public void handle(final Packet packet) {
29 | if (packet.isFlying() && !isExempt(ExemptType.TPS)) {
30 | final long now = now();
31 | final long delta = now - lastFlyingTime;
32 |
33 | if (delta > 1) {
34 | samples.add(delta);
35 | }
36 |
37 | if (samples.isFull()) {
38 | final double average = MathUtil.getAverage(samples);
39 | final double speed = 50 / average;
40 |
41 | debug(String.format("speed=%.4f, delta=%o, buffer=%.2f", speed, delta, buffer));
42 |
43 | if (speed >= maxTimerSpeed.getDouble()) {
44 | if (++buffer > 30) {
45 | buffer = Math.min(buffer, 50);
46 | fail(String.format("speed=%.4f, delta=%o, buffer=%.2f", speed, delta, buffer));
47 | }
48 | } else {
49 | buffer = Math.max(0, buffer - 1);
50 | }
51 | }
52 |
53 | lastFlyingTime = now;
54 | } else if (packet.isOutPosition()) {
55 | samples.add(135L); //Magic value. 100L doesn't completely fix it for some reason.
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/player/timer/TimerB.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.player.timer;
2 |
3 | import ai.medusa.anticheat.check.Check;
4 | import ai.medusa.api.check.CheckInfo;
5 | import ai.medusa.anticheat.config.ConfigValue;
6 | import ai.medusa.anticheat.data.PlayerData;
7 | import ai.medusa.anticheat.exempt.type.ExemptType;
8 | import ai.medusa.anticheat.packet.Packet;
9 | import ai.medusa.anticheat.util.MathUtil;
10 | import ai.medusa.anticheat.util.type.EvictingList;
11 |
12 | /**
13 | * Created on 11/13/2020 Package ai.medusa.anticheat.check.impl.player.timer by infinitesm
14 | */
15 |
16 | @CheckInfo(name = "Timer (B)", description = "Checks for game speed which is too slow.", experimental = true)
17 | public final class TimerB extends Check {
18 |
19 | private static final ConfigValue minSpeed = new ConfigValue(ConfigValue.ValueType.DOUBLE, "minimum-timer-speed");
20 | private final EvictingList samples = new EvictingList<>(50);
21 | private long lastFlyingTime;
22 |
23 |
24 | public TimerB(final PlayerData data) {
25 | super(data);
26 | }
27 |
28 | @Override
29 | public void handle(final Packet packet) {
30 | if (packet.isFlying() && !isExempt(ExemptType.TPS)) {
31 | final long now = now();
32 | final long delta = now - lastFlyingTime;
33 |
34 | samples.add(delta);
35 | if (samples.isFull()) {
36 | final double average = samples.stream().mapToDouble(value -> value).average().orElse(1);
37 | final double speed = 50 / average;
38 |
39 | final double deviation = MathUtil.getStandardDeviation(samples);
40 |
41 | if (speed <= minSpeed.getDouble() && deviation < 50.0) {
42 | if (++buffer > 35) {
43 | fail(String.format("speed=%.2f deviation=%.2f", speed, deviation));
44 | }
45 | } else {
46 | buffer /= 2;
47 | }
48 | }
49 | lastFlyingTime = now;
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/check/impl/player/timer/TimerC.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.check.impl.player.timer;
2 |
3 | import ai.medusa.anticheat.check.Check;
4 | import ai.medusa.api.check.CheckInfo;
5 | import ai.medusa.anticheat.config.ConfigValue;
6 | import ai.medusa.anticheat.data.PlayerData;
7 | import ai.medusa.anticheat.exempt.type.ExemptType;
8 | import ai.medusa.anticheat.packet.Packet;
9 |
10 | @CheckInfo(name = "Timer (C)", description = "Balance based timer check.", experimental = true)
11 | public final class TimerC extends Check {
12 |
13 | private static final ConfigValue maxBal = new ConfigValue(ConfigValue.ValueType.LONG, "maximum-balance");
14 | private static final ConfigValue balReset = new ConfigValue(ConfigValue.ValueType.LONG, "balance-reset");
15 | private static final ConfigValue balSubOnTp = new ConfigValue(
16 | ConfigValue.ValueType.LONG, "balance-subtraction-on-teleport"
17 | );
18 | private long lastFlyingTime = 0L;
19 | private long balance = 0L;
20 |
21 | public TimerC(final PlayerData data) {
22 | super(data);
23 | }
24 |
25 | @Override
26 | public void handle(final Packet packet) {
27 | if (packet.isFlying() && !isExempt(ExemptType.JOINED, ExemptType.TPS)) {
28 | if (lastFlyingTime != 0L) {
29 | final long now = now();
30 | balance += 50L;
31 | balance -= now - lastFlyingTime;
32 | if (balance > maxBal.getLong()) {
33 | fail("balance=" + balance);
34 | balance = balReset.getLong();
35 | }
36 | }
37 | lastFlyingTime = now();
38 | } else if (packet.isOutPosition()) {
39 | balance -= balSubOnTp.getLong();
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/command/CommandInfo.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.command;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | @Retention(RetentionPolicy.RUNTIME)
9 | @Target(ElementType.TYPE)
10 | public @interface CommandInfo {
11 | String name();
12 | String purpose();
13 | String syntax() default "";
14 | }
15 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/command/CommandManager.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.command;
2 |
3 | import ai.medusa.anticheat.MedusaPlugin;
4 | import ai.medusa.anticheat.command.impl.*;
5 | import ai.medusa.anticheat.command.impl.*;
6 | import ai.medusa.anticheat.command.impl.*;
7 | import ai.medusa.anticheat.command.impl.*;
8 | import ai.medusa.anticheat.util.ColorUtil;
9 | import ai.medusa.anticheat.config.Config;
10 | import org.bukkit.command.Command;
11 | import org.bukkit.command.CommandExecutor;
12 | import org.bukkit.command.CommandSender;
13 |
14 | import java.util.ArrayList;
15 | import java.util.List;
16 |
17 | public final class CommandManager implements CommandExecutor {
18 |
19 | private final List commands = new ArrayList<>();
20 |
21 | public CommandManager(final MedusaPlugin plugin) {
22 | commands.add(new Alerts());
23 | commands.add(new Info());
24 | commands.add(new Debug());
25 | commands.add(new Violations());
26 | commands.add(new Theme());
27 | }
28 |
29 | @Override
30 | public boolean onCommand(CommandSender commandSender, Command command, String string, String[] args) {
31 | if (commandSender.hasPermission("medusa.commands")) {
32 | if (args.length > 0) {
33 | for (final MedusaCommand medusaCommand : commands) {
34 | final String commandName = medusaCommand.getCommandInfo().name();
35 | if (commandName.equals(args[0])) {
36 | if (commandSender.hasPermission("medusa." + commandName)) {
37 | if (!medusaCommand.handle(commandSender, command, string, args)) {
38 | commandSender.sendMessage(ColorUtil.translate(Config.ACCENT_ONE + "Usage: /medusa " +
39 | medusaCommand.getCommandInfo().name() + " " +
40 | medusaCommand.getCommandInfo().syntax()));
41 | }
42 | } else {
43 | return false;
44 | }
45 | return true;
46 | }
47 | }
48 | } else {
49 | commandSender.sendMessage("");
50 | commandSender.sendMessage(ColorUtil.translate(Config.ACCENT_ONE + "Medusa AntiCheat Commands:\n" + " \n"));
51 | for (final MedusaCommand medusaCommand : commands) {
52 | commandSender.sendMessage(ColorUtil.translate( Config.ACCENT_ONE + "/medusa " +
53 | medusaCommand.getCommandInfo().name() + " " +
54 | medusaCommand.getCommandInfo().syntax()));
55 | }
56 | commandSender.sendMessage("");
57 | return true;
58 | }
59 | }
60 | return false;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/command/MedusaCommand.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.command;
2 |
3 | import ai.medusa.anticheat.util.ColorUtil;
4 | import ai.medusa.anticheat.config.Config;
5 | import org.bukkit.command.Command;
6 | import org.bukkit.command.CommandSender;
7 |
8 | public abstract class MedusaCommand implements Comparable {
9 |
10 | protected abstract boolean handle(final CommandSender sender, final Command command, final String label, final String[] args);
11 |
12 | public void sendLineBreak(final CommandSender sender) {
13 | sender.sendMessage(ColorUtil.translate(Config.ACCENT_TWO + "&m----------------------------------------------"));
14 | }
15 |
16 | public void sendRetardedNewLine(final CommandSender sender) {
17 | sender.sendMessage("");
18 | }
19 |
20 | public void sendMessage(final CommandSender sender, final String message) {
21 | sender.sendMessage(ColorUtil.translate(message));
22 | }
23 |
24 | public CommandInfo getCommandInfo() {
25 | if (this.getClass().isAnnotationPresent(CommandInfo.class)) {
26 | return this.getClass().getAnnotation(CommandInfo.class);
27 | } else {
28 | System.err.println("CommandInfo annotation hasn't been added to the class " + this.getClass().getSimpleName() + ".");
29 | }
30 | return null;
31 | }
32 |
33 | @Override
34 | public int compareTo(MedusaCommand o) {
35 | return 0;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/command/impl/Alerts.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.command.impl;
2 |
3 | import ai.medusa.anticheat.Medusa;
4 | import ai.medusa.anticheat.command.MedusaCommand;
5 | import ai.medusa.anticheat.config.Config;
6 | import ai.medusa.anticheat.data.PlayerData;
7 | import ai.medusa.anticheat.util.anticheat.AlertUtil;
8 | import ai.medusa.anticheat.command.CommandInfo;
9 | import org.bukkit.command.Command;
10 | import org.bukkit.command.CommandSender;
11 | import org.bukkit.entity.Player;
12 |
13 | @CommandInfo(name = "alerts", purpose = "Toggles cheat alerts.")
14 | public final class Alerts extends MedusaCommand {
15 |
16 | @Override
17 | protected boolean handle(CommandSender sender, Command command, String label, String[] args) {
18 | if (sender instanceof Player) {
19 | final Player player = (Player) sender;
20 | final PlayerData data = Medusa.INSTANCE.getPlayerDataManager().getPlayerData(player);
21 |
22 | if (data != null) {
23 | if (AlertUtil.toggleAlerts(data) == AlertUtil.ToggleAlertType.ADD) {
24 | sendMessage(sender, Config.ACCENT_ONE + "Toggled your cheat alerts &2on" + Config.ACCENT_ONE + ".");
25 | } else {
26 | sendMessage(sender, Config.ACCENT_ONE + "Toggled your cheat alerts &coff" + Config.ACCENT_ONE + ".");
27 | }
28 | return true;
29 | }
30 | } else {
31 | sendMessage(sender, "Only players can execute this command.");
32 | }
33 | return false;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/command/impl/Debug.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.command.impl;
2 |
3 | import ai.medusa.anticheat.Medusa;
4 | import ai.medusa.anticheat.check.Check;
5 | import ai.medusa.anticheat.command.CommandInfo;
6 | import ai.medusa.anticheat.command.MedusaCommand;
7 | import ai.medusa.anticheat.data.PlayerData;
8 | import org.bukkit.ChatColor;
9 | import org.bukkit.command.Command;
10 | import org.bukkit.command.CommandSender;
11 | import org.bukkit.entity.Player;
12 |
13 | @CommandInfo(name = "debug", syntax = "", purpose = "Debug information from a specified check.")
14 | public class Debug extends MedusaCommand {
15 | @Override
16 | protected boolean handle(final CommandSender sender, final Command command, final String label, final String[] args) {
17 | if (sender instanceof Player) {
18 | final PlayerData data = Medusa.INSTANCE.getPlayerDataManager().getPlayerData(((Player) sender).getPlayer());
19 |
20 | if (data != null) {
21 | if (args.length == 3) {
22 | final String checkName = args[1] + " " + args[2];
23 |
24 | for (Check check : data.getChecks()) {
25 | if (check.getCheckInfo().name().equals(checkName)) {
26 | check.setDebugging(true);
27 | sender.sendMessage(ChatColor.GREEN + "Debugging " + checkName + "!");
28 | } else {
29 | check.setDebugging(false);
30 | }
31 | }
32 | return true;
33 | } else if (args.length == 2) {
34 | if (args[1].equalsIgnoreCase("none")) {
35 | for (Check check : data.getChecks()) {
36 | check.setDebugging(false);
37 | }
38 | sender.sendMessage(ChatColor.RED + "Turned off debugging!");
39 | return true;
40 | }
41 | }
42 | }
43 | }
44 | sender.sendMessage(ChatColor.RED + "Invalid check!");
45 | return false;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/command/impl/Info.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.command.impl;
2 |
3 | import ai.medusa.anticheat.Medusa;
4 | import ai.medusa.anticheat.command.CommandInfo;
5 | import ai.medusa.anticheat.command.MedusaCommand;
6 | import ai.medusa.anticheat.config.Config;
7 | import ai.medusa.anticheat.data.PlayerData;
8 | import ai.medusa.anticheat.util.ColorUtil;
9 | import io.github.retrooper.packetevents.PacketEvents;
10 | import org.bukkit.Bukkit;
11 | import org.bukkit.command.Command;
12 | import org.bukkit.command.CommandSender;
13 | import org.bukkit.entity.Player;
14 |
15 | @CommandInfo(name = "info", syntax = "", purpose = "Returns information about the players client.")
16 | public final class Info extends MedusaCommand {
17 | @Override
18 | protected boolean handle(final CommandSender sender, final Command command, final String label, final String[] args) {
19 | if (args.length == 2) {
20 | final Player player = Bukkit.getPlayer(args[1]);
21 |
22 | if (player != null) {
23 | final PlayerData playerData = Medusa.INSTANCE.getPlayerDataManager().getPlayerData(player);
24 |
25 | if (playerData != null) {
26 | sendRetardedNewLine(sender);
27 | sendMessage(sender, Config.ACCENT_ONE + "Information for &c" + playerData.getPlayer().getName() + Config.ACCENT_ONE + ".");
28 | sendRetardedNewLine(sender);
29 | sendMessage(sender, Config.ACCENT_TWO + "&oGeneral information:");
30 | sendMessage(sender, Config.ACCENT_ONE + "Latency → " + Config.ACCENT_TWO + PacketEvents.get().getPlayerUtils().getPing(playerData.getPlayer()) + "ms");
31 | sendMessage(sender, Config.ACCENT_ONE + "Checks amount → " + Config.ACCENT_TWO + playerData.getChecks().size());
32 | sendMessage(sender, Config.ACCENT_ONE + "Sensitivity → " + Config.ACCENT_TWO + playerData.getRotationProcessor().getSensitivity() + "%");
33 | final String clientBrand = playerData.getClientBrand() == null ? "&cCould not resolve client brand for this player." : playerData.getClientBrand();
34 | sendMessage(sender, ColorUtil.translate(Config.ACCENT_ONE + "Client brand: → " + Config.ACCENT_TWO + clientBrand));
35 | sendRetardedNewLine(sender);
36 | return true;
37 | }
38 | }
39 | }
40 | return false;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/command/impl/Theme.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.command.impl;
2 |
3 | import ai.medusa.anticheat.command.CommandInfo;
4 | import ai.medusa.anticheat.command.MedusaCommand;
5 | import ai.medusa.anticheat.config.Config;
6 | import ai.medusa.anticheat.manager.ThemeManager;
7 | import org.bukkit.command.Command;
8 | import org.bukkit.command.CommandSender;
9 |
10 |
11 | @CommandInfo(name = "theme", syntax = "", purpose = "Changes the theme.")
12 | public final class Theme extends MedusaCommand {
13 | @Override
14 | protected boolean handle(final CommandSender sender, final Command command, final String label, final String[] args) {
15 | if (args.length == 2) {
16 | for (ThemeManager.Theme theme : ThemeManager.themes) {
17 | if (args[1].equalsIgnoreCase(theme.getName())) {
18 | Config.ALERT_FORMAT = theme.getAlertFormat();
19 | Config.ACCENT_ONE = "&" + theme.getAccentColours().get(0).getChar();
20 | Config.ACCENT_TWO = "&" + theme.getAccentColours().get(1).getChar();
21 |
22 | sendMessage(sender, Config.ACCENT_ONE + "Updated theme to &c" + theme.getName() + " theme.");
23 | return true;
24 | }
25 | }
26 | } else if (args.length == 1) {
27 | sendRetardedNewLine(sender);
28 | sendMessage(sender, Config.ACCENT_ONE + "Themes for the anti-cheat:");
29 | sendRetardedNewLine(sender);
30 | for (ThemeManager.Theme theme : ThemeManager.themes) {
31 | sendMessage(sender, Config.ACCENT_ONE + theme.getName());
32 | }
33 | sendRetardedNewLine(sender);
34 | return true;
35 | }
36 | return false;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/command/impl/Violations.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.command.impl;
2 |
3 | import ai.medusa.anticheat.Medusa;
4 | import ai.medusa.anticheat.command.CommandInfo;
5 | import ai.medusa.anticheat.command.MedusaCommand;
6 | import ai.medusa.anticheat.config.Config;
7 | import ai.medusa.anticheat.data.PlayerData;
8 | import org.bukkit.Bukkit;
9 | import org.bukkit.command.Command;
10 | import org.bukkit.command.CommandSender;
11 | import org.bukkit.entity.Player;
12 |
13 | @CommandInfo(name = "violations", syntax = "", purpose = "Describes violations for the player.")
14 | public final class Violations extends MedusaCommand {
15 | @Override
16 | protected boolean handle(final CommandSender sender, final Command command, final String label, final String[] args) {
17 | if (args.length == 2) {
18 | final Player player = Bukkit.getPlayer(args[1]);
19 |
20 | if (player != null) {
21 | final PlayerData playerData = Medusa.INSTANCE.getPlayerDataManager().getPlayerData(player);
22 |
23 | if (playerData != null) {
24 | sendRetardedNewLine(sender);
25 | sendMessage(sender, Config.ACCENT_ONE + "Violations for &c" + playerData.getPlayer().getName() + Config.ACCENT_ONE + ".");
26 | sendRetardedNewLine(sender);
27 | sendMessage(sender, Config.ACCENT_ONE + "Total check violations → " + Config.ACCENT_TWO + playerData.getTotalViolations());
28 | sendMessage(sender, Config.ACCENT_ONE + "Combat check violations → " + Config.ACCENT_TWO + playerData.getCombatViolations());
29 | sendMessage(sender, Config.ACCENT_ONE + "Movement check violations → " + Config.ACCENT_TWO + playerData.getMovementViolations());
30 | sendMessage(sender, Config.ACCENT_ONE + "Player check violations → " + Config.ACCENT_TWO + playerData.getPlayerViolations());
31 | sendRetardedNewLine(sender);
32 | return true;
33 | }
34 | }
35 | }
36 | return false;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/config/ConfigValue.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.config;
2 |
3 | import lombok.Getter;
4 | import lombok.Setter;
5 |
6 | public final class ConfigValue {
7 | @Setter
8 | private Object value;
9 | @Getter
10 | private final ValueType type;
11 | @Getter
12 | private final String name;
13 |
14 | public ConfigValue(ValueType type, String name) {
15 | this.type = type;
16 | this.name = name;
17 | }
18 |
19 | public boolean getBoolean() {
20 | return (boolean) value;
21 | }
22 |
23 | public double getDouble() {
24 | return (double) value;
25 | }
26 |
27 | public int getInt() {
28 | return (int) value;
29 | }
30 |
31 | public long getLong() {
32 | return (long) value;
33 | }
34 |
35 | public String getString() {
36 | return (String) value;
37 | }
38 |
39 | public enum ValueType {
40 | INTEGER,
41 | DOUBLE,
42 | BOOLEAN,
43 | STRING,
44 | LONG
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/data/PlayerData.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.data;
2 |
3 | import ai.medusa.anticheat.check.Check;
4 | import ai.medusa.anticheat.data.processor.*;
5 | import ai.medusa.anticheat.data.processor.*;
6 | import ai.medusa.anticheat.data.processor.*;
7 | import ai.medusa.anticheat.data.processor.*;
8 | import ai.medusa.anticheat.util.type.EvictingList;
9 | import ai.medusa.anticheat.util.type.Pair;
10 | import lombok.Getter;
11 | import lombok.Setter;
12 | import ai.medusa.anticheat.exempt.ExemptProcessor;
13 | import ai.medusa.anticheat.manager.CheckManager;
14 | import org.bukkit.Location;
15 | import org.bukkit.entity.Player;
16 | import java.util.List;
17 |
18 | @Getter
19 | @Setter
20 | public final class PlayerData {
21 |
22 | private final Player player;
23 | private String clientBrand;
24 | private int totalViolations, combatViolations, movementViolations, playerViolations;
25 | private final long joinTime = System.currentTimeMillis();
26 | private final List checks = CheckManager.loadChecks(this);
27 | private final EvictingList> targetLocations = new EvictingList<>(40);
28 |
29 | private final ExemptProcessor exemptProcessor = new ExemptProcessor(this);
30 | private final CombatProcessor combatProcessor = new CombatProcessor(this);
31 | private final ActionProcessor actionProcessor = new ActionProcessor(this);
32 | private final ClickProcessor clickProcessor = new ClickProcessor(this);
33 | private final PositionProcessor positionProcessor = new PositionProcessor(this);
34 | private final RotationProcessor rotationProcessor = new RotationProcessor(this);
35 | private final VelocityProcessor velocityProcessor = new VelocityProcessor(this);
36 |
37 | public PlayerData(final Player player) {
38 | this.player = player;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/data/processor/ActionProcessor.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.data.processor;
2 |
3 | import ai.medusa.anticheat.Medusa;
4 | import ai.medusa.anticheat.util.MathUtil;
5 | import ai.medusa.anticheat.util.PlayerUtil;
6 | import ai.medusa.anticheat.util.type.EvictingList;
7 | import io.github.retrooper.packetevents.PacketEvents;
8 | import io.github.retrooper.packetevents.packetwrappers.play.in.blockdig.WrappedPacketInBlockDig;
9 | import io.github.retrooper.packetevents.packetwrappers.play.in.clientcommand.WrappedPacketInClientCommand;
10 | import io.github.retrooper.packetevents.packetwrappers.play.in.entityaction.WrappedPacketInEntityAction;
11 | import io.github.retrooper.packetevents.packetwrappers.play.in.helditemslot.WrappedPacketInHeldItemSlot;
12 | import lombok.Getter;
13 | import ai.medusa.anticheat.data.PlayerData;
14 | import lombok.Setter;
15 | import org.bukkit.event.block.Action;
16 | import org.bukkit.event.player.PlayerInteractEvent;
17 |
18 | @Getter
19 | public final class ActionProcessor {
20 |
21 | private final PlayerData data;
22 |
23 | private final EvictingList flyingSamples = new EvictingList<>(50);
24 |
25 | private boolean sprinting, sneaking, sendingAction, placing, digging, blocking,
26 | respawning, sendingDig, lagging;
27 |
28 | @Setter private boolean inventory;
29 |
30 | private int heldItemSlot, lastHeldItemSlot, lastDiggingTick, lastPlaceTick, lastBreakTick,
31 | sprintingTicks, sneakingTicks;
32 |
33 | private long lastFlyingTime, ping;
34 |
35 | public ActionProcessor(final PlayerData data) {
36 | this.data = data;
37 | }
38 |
39 | public void handleEntityAction(final WrappedPacketInEntityAction wrapper) {
40 | sendingAction = true;
41 | switch (wrapper.getAction()) {
42 | case START_SPRINTING:
43 | sprinting = true;
44 | break;
45 | case STOP_SPRINTING:
46 | sprinting = false;
47 | break;
48 | case START_SNEAKING:
49 | sneaking = true;
50 | break;
51 | case STOP_SNEAKING:
52 | sneaking = false;
53 | break;
54 | }
55 | }
56 |
57 | public void handleBlockDig(final WrappedPacketInBlockDig wrapper) {
58 | sendingDig = true;
59 | switch (wrapper.getDigType()) {
60 | case START_DESTROY_BLOCK:
61 | digging = true;
62 | break;
63 | case STOP_DESTROY_BLOCK:
64 | case ABORT_DESTROY_BLOCK:
65 | digging = false;
66 | break;
67 | case RELEASE_USE_ITEM:
68 | blocking = true;
69 | break;
70 | }
71 | }
72 |
73 | public void handleClientCommand(final WrappedPacketInClientCommand wrapper) {
74 | switch (wrapper.getClientCommand()) {
75 | case OPEN_INVENTORY_ACHIEVEMENT:
76 | inventory = true;
77 | break;
78 | case PERFORM_RESPAWN:
79 | respawning = true;
80 | break;
81 | }
82 | }
83 |
84 | public void handleHeldItemSlot(final WrappedPacketInHeldItemSlot wrapper) {
85 | this.lastHeldItemSlot = this.heldItemSlot;
86 | this.heldItemSlot = wrapper.getCurrentSelectedSlot();
87 | }
88 |
89 | public void handleBlockPlace() {
90 | placing = true;
91 | }
92 |
93 | public void handleCloseWindow() {
94 | inventory = false;
95 | }
96 |
97 | public void handleArmAnimation() {
98 | /*
99 | This can be disabled if the client sends a dig packet then immediately start clicking
100 | Which makes it so the player is immune to AutoClicker checks due to his Digging state.
101 | Getting the looking block ensures that the player is not spoofing his digging state.
102 | */
103 | if (digging && PlayerUtil.getLookingBlock(data.getPlayer(), 5) != null) {
104 | lastDiggingTick = Medusa.INSTANCE.getTickManager().getTicks();
105 | }
106 | }
107 |
108 | public void handleInteract(final PlayerInteractEvent event) {
109 | if (event.getAction() == Action.LEFT_CLICK_BLOCK) {
110 | lastDiggingTick = Medusa.INSTANCE.getTickManager().getTicks();
111 | }
112 | }
113 |
114 | public void handleBukkitPlace() {
115 | lastPlaceTick = Medusa.INSTANCE.getTickManager().getTicks();
116 | }
117 |
118 | public void handleBukkitBlockBreak() {
119 | lastBreakTick = Medusa.INSTANCE.getTickManager().getTicks();
120 | }
121 |
122 | public void handleFlying() {
123 | blocking = false;
124 | sendingDig = false;
125 | sendingAction = false;
126 | placing = false;
127 | respawning = false;
128 |
129 | sprintingTicks = sprinting ? sprintingTicks + 1 : 0;
130 | sneakingTicks = sneaking ? sneakingTicks + 1 : 0;
131 |
132 | final long delay = System.currentTimeMillis() - lastFlyingTime;
133 |
134 | if (delay > 0) {
135 | flyingSamples.add(delay);
136 | }
137 |
138 | if (flyingSamples.isFull()) {
139 | final double deviation = MathUtil.getStandardDeviation(flyingSamples);
140 | lagging = deviation > 120;
141 | }
142 | lastFlyingTime = System.currentTimeMillis();
143 | ping = PacketEvents.get().getPlayerUtils().getPing(data.getPlayer());
144 | }
145 | }
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/data/processor/ClickProcessor.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.data.processor;
2 |
3 | import lombok.Getter;
4 | import ai.medusa.anticheat.data.PlayerData;
5 |
6 | @Getter
7 | public final class ClickProcessor {
8 |
9 | private final PlayerData data;
10 | private long lastSwing = -1;
11 | private long delay;
12 |
13 | public ClickProcessor(final PlayerData data) {
14 | this.data = data;
15 | }
16 |
17 | public void handleArmAnimation() {
18 | if (!data.getActionProcessor().isDigging() && !data.getActionProcessor().isPlacing()) {
19 | if (lastSwing > 0) {
20 | delay = System.currentTimeMillis() - lastSwing;
21 | }
22 | lastSwing = System.currentTimeMillis();
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/data/processor/CombatProcessor.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.data.processor;
2 |
3 | import io.github.retrooper.packetevents.packetwrappers.play.in.useentity.WrappedPacketInUseEntity;
4 | import lombok.Getter;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import org.bukkit.entity.Entity;
7 |
8 |
9 | @Getter
10 | public final class CombatProcessor {
11 |
12 | private final PlayerData data;
13 |
14 | private int hitTicks, swings, hits, currentTargets;
15 |
16 | private double hitMissRatio, distance;
17 |
18 | private Entity target, lastTarget;
19 |
20 | public CombatProcessor(final PlayerData data) {
21 | this.data = data;
22 | }
23 |
24 | public void handleUseEntity(final WrappedPacketInUseEntity wrapper) {
25 | if (wrapper.getAction() != WrappedPacketInUseEntity.EntityUseAction.ATTACK || wrapper.getEntity() == null) {
26 | return;
27 | }
28 |
29 | lastTarget = target == null ? wrapper.getEntity() : target;
30 | target = wrapper.getEntity();
31 |
32 | distance = data.getPlayer().getLocation().toVector().setY(0).distance(target.getLocation().toVector().setY(0)) - .42;
33 |
34 | ++hits;
35 |
36 | hitTicks = 0;
37 |
38 | if (target != lastTarget) {
39 | ++currentTargets;
40 | }
41 | }
42 |
43 | public void handleArmAnimation() {
44 | ++swings;
45 | }
46 |
47 | public void handleFlying() {
48 | ++hitTicks;
49 | currentTargets = 0;
50 |
51 | if (swings > 1) {
52 | hitMissRatio = ((double) hits / (double) swings) * 100;
53 | }
54 | if (hits > 100 || swings > 100) {
55 | hits = swings = 0;
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/data/processor/RotationProcessor.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.data.processor;
2 |
3 | import ai.medusa.anticheat.util.MathUtil;
4 | import lombok.Getter;
5 | import ai.medusa.anticheat.Medusa;
6 | import ai.medusa.anticheat.data.PlayerData;
7 |
8 | import java.util.ArrayDeque;
9 |
10 | @Getter
11 | public final class RotationProcessor {
12 |
13 | private final PlayerData data;
14 |
15 | private float yaw, pitch, lastYaw, lastPitch,
16 | deltaYaw, deltaPitch, lastDeltaYaw, lastDeltaPitch,
17 | joltYaw, joltPitch, lastJoltYaw, lastJoltPitch, gcd;
18 |
19 | private int sensitivity, lastCinematic, cinematicTicks;
20 |
21 | private final ArrayDeque sensitivitySamples = new ArrayDeque<>();
22 |
23 | private boolean cinematic;
24 |
25 | private double finalSensitivity;
26 |
27 | public RotationProcessor(final PlayerData data) {
28 | this.data = data;
29 | }
30 |
31 | public void handle(final float yaw, final float pitch) {
32 | lastYaw = this.yaw;
33 | lastPitch = this.pitch;
34 |
35 | this.yaw = yaw;
36 | this.pitch = pitch;
37 |
38 | lastDeltaYaw = deltaYaw;
39 | lastDeltaPitch = deltaPitch;
40 |
41 | deltaYaw = Math.abs(yaw - lastYaw) % 360F;
42 | deltaPitch = Math.abs(pitch - lastPitch);
43 |
44 | lastJoltPitch = joltPitch;
45 | lastJoltYaw = joltYaw;
46 |
47 | joltYaw = Math.abs(deltaYaw - lastDeltaYaw);
48 | joltPitch = Math.abs(deltaPitch - lastDeltaPitch);
49 |
50 | processCinematic();
51 |
52 | if (deltaPitch > 0 && deltaPitch < 30) {
53 | processSensitivity();
54 | }
55 | }
56 |
57 | private void processCinematic() {
58 | final float yawAccelAccel = Math.abs(joltYaw - lastJoltYaw);
59 | final float pitchAccelAccel = Math.abs(joltPitch - lastJoltPitch);
60 |
61 | final boolean invalidYaw = yawAccelAccel < .05 && yawAccelAccel > 0;
62 | final boolean invalidPitch = pitchAccelAccel < .05 && pitchAccelAccel > 0;
63 |
64 | final boolean exponentialYaw = String.valueOf(yawAccelAccel).contains("E");
65 | final boolean exponentialPitch = String.valueOf(pitchAccelAccel).contains("E");
66 |
67 | if (sensitivity < 100 && (exponentialYaw || exponentialPitch)) {
68 | cinematicTicks += 3;
69 | } else if (invalidYaw || invalidPitch) {
70 | cinematicTicks += 1;
71 | } else {
72 | if (cinematicTicks > 0) cinematicTicks--;
73 | }
74 | if (cinematicTicks > 20) {
75 | cinematicTicks--;
76 | }
77 |
78 | cinematic = cinematicTicks > 8 || (Medusa.INSTANCE.getTickManager().getTicks() - lastCinematic < 120);
79 |
80 | if (cinematic && cinematicTicks > 8) {
81 | lastCinematic = Medusa.INSTANCE.getTickManager().getTicks();
82 | }
83 | }
84 |
85 | private void processSensitivity() {
86 | final float gcd = (float) MathUtil.getGcd(deltaPitch, lastDeltaPitch);
87 | final double sensitivityModifier = Math.cbrt(0.8333 * gcd);
88 | final double sensitivityStepTwo = (sensitivityModifier / 0.6) - 0.3333;
89 | final double finalSensitivity = sensitivityStepTwo * 200;
90 |
91 | this.finalSensitivity = finalSensitivity;
92 |
93 | sensitivitySamples.add((int)finalSensitivity);
94 |
95 | if (sensitivitySamples.size() >= 40) {
96 | this.sensitivity = MathUtil.getMode(sensitivitySamples);
97 |
98 | final float gcdOne = (sensitivity / 200F) * 0.6F + 0.2F;
99 | this.gcd = gcdOne * gcdOne * gcdOne * 1.2F;
100 |
101 | sensitivitySamples.clear();
102 | }
103 | }
104 | }
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/data/processor/VelocityProcessor.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.data.processor;
2 |
3 | import io.github.retrooper.packetevents.PacketEvents;
4 | import io.github.retrooper.packetevents.packetwrappers.play.in.transaction.WrappedPacketInTransaction;
5 | import io.github.retrooper.packetevents.packetwrappers.play.out.transaction.WrappedPacketOutTransaction;
6 | import lombok.Getter;
7 | import ai.medusa.anticheat.Medusa;
8 | import ai.medusa.anticheat.data.PlayerData;
9 |
10 | import java.util.concurrent.ThreadLocalRandom;
11 |
12 | @Getter
13 | public final class VelocityProcessor {
14 |
15 | private final PlayerData data;
16 | private double velocityX, velocityY, velocityZ, lastVelocityX, lastVelocityY, lastVelocityZ;
17 | private int maxVelocityTicks, velocityTicks, ticksSinceVelocity;
18 | private short transactionID, velocityID;
19 | private long transactionPing, transactionReply;
20 | private boolean verifyingVelocity;
21 |
22 | public VelocityProcessor(final PlayerData data) {
23 | this.data = data;
24 | }
25 |
26 | public void handle(final double velocityX, final double velocityY, final double velocityZ) {
27 | this.ticksSinceVelocity = 0;
28 |
29 | lastVelocityX = this.velocityX;
30 | lastVelocityY = this.velocityY;
31 | lastVelocityZ = this.velocityZ;
32 |
33 | this.velocityX = velocityX;
34 | this.velocityY = velocityY;
35 | this.velocityZ = velocityZ;
36 |
37 | this.velocityID = (short) ThreadLocalRandom.current().nextInt(32767);
38 | this.verifyingVelocity = true;
39 |
40 | PacketEvents.get().getPlayerUtils().sendPacket(data.getPlayer(), new WrappedPacketOutTransaction(0, velocityID, false));
41 | }
42 |
43 | public void handleTransaction(final WrappedPacketInTransaction wrapper) {
44 |
45 | if (this.verifyingVelocity && wrapper.getActionNumber() == this.velocityID) {
46 | this.verifyingVelocity = false;
47 | this.velocityTicks = Medusa.INSTANCE.getTickManager().getTicks();
48 | this.maxVelocityTicks = (int) (((lastVelocityZ + lastVelocityX) / 2 + 2) * 15);
49 | }
50 |
51 | if (wrapper.getActionNumber() == transactionID) {
52 | transactionPing = System.currentTimeMillis() - transactionReply;
53 |
54 | transactionID = (short) ThreadLocalRandom.current().nextInt(32767);
55 | PacketEvents.get().getPlayerUtils().sendPacket(data.getPlayer(), new WrappedPacketOutTransaction(0, transactionID, false));
56 | transactionReply = System.currentTimeMillis();
57 | }
58 | }
59 |
60 | public void handleFlying() {
61 | ++ticksSinceVelocity;
62 | }
63 |
64 | public boolean isTakingVelocity() {
65 | return Math.abs(Medusa.INSTANCE.getTickManager().getTicks() - this.velocityTicks) < this.maxVelocityTicks;
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/exempt/ExemptProcessor.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.exempt;
2 |
3 | import ai.medusa.anticheat.data.PlayerData;
4 | import ai.medusa.anticheat.exempt.type.ExemptType;
5 | import lombok.RequiredArgsConstructor;
6 |
7 | import java.util.Arrays;
8 | import java.util.function.Function;
9 |
10 | @RequiredArgsConstructor
11 | public final class ExemptProcessor {
12 |
13 | private final PlayerData data;
14 |
15 | public boolean isExempt(final ExemptType exemptType) {
16 | return exemptType.getException().apply(data);
17 | }
18 |
19 | public boolean isExempt(final ExemptType... exemptTypes) {
20 | return Arrays.stream(exemptTypes).anyMatch(this::isExempt);
21 | }
22 |
23 | public boolean isExempt(final Function exception) {
24 | return exception.apply(data);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/exempt/type/ExemptType.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.exempt.type;
2 |
3 | import ai.medusa.anticheat.Medusa;
4 | import ai.medusa.anticheat.data.PlayerData;
5 | import ai.medusa.anticheat.util.PlayerUtil;
6 | import ai.medusa.anticheat.util.ServerUtil;
7 | import lombok.Getter;
8 |
9 | import java.util.function.Function;
10 |
11 | @Getter
12 | public enum ExemptType {
13 |
14 | CHUNK(data -> !data.getPlayer().getWorld().isChunkLoaded(data.getPlayer().getLocation().getBlockX() << 4,
15 | data.getPlayer().getLocation().getBlockZ() << 4)),
16 |
17 | TPS(data -> ServerUtil.getTPS() < 18.5D),
18 |
19 | TELEPORT(data -> data.getPositionProcessor().isTeleporting() || System.currentTimeMillis() - data.getJoinTime() < 2000L),
20 |
21 | VELOCITY(data -> data.getVelocityProcessor().isTakingVelocity()),
22 |
23 | JOINED(data -> System.currentTimeMillis() - data.getJoinTime() < 5000L),
24 |
25 | TRAPDOOR(data -> data.getPositionProcessor().isNearTrapdoor()),
26 |
27 | STEPPED(data -> data.getPositionProcessor().isOnGround() && data.getPositionProcessor().getDeltaY() > 0),
28 |
29 | CINEMATIC(data -> data.getRotationProcessor().isCinematic()),
30 |
31 | SLIME(data -> data.getPositionProcessor().getSinceSlimeTicks() < 30),
32 |
33 | ICE(data -> data.getPositionProcessor().getSinceIceTicks() < 40),
34 |
35 | SLAB(data -> data.getPositionProcessor().isNearSlab()),
36 |
37 | STAIRS(data -> data.getPositionProcessor().isNearStairs()),
38 |
39 | WEB(data -> data.getPositionProcessor().isInWeb()),
40 |
41 | CLIMBABLE(data -> data.getPositionProcessor().isOnClimbable()),
42 |
43 | DIGGING(data -> Medusa.INSTANCE.getTickManager().getTicks() - data.getActionProcessor().getLastDiggingTick() < 10),
44 |
45 | BLOCK_BREAK(data -> Medusa.INSTANCE.getTickManager().getTicks() - data.getActionProcessor().getLastBreakTick() < 10),
46 |
47 | PLACING(data -> Medusa.INSTANCE.getTickManager().getTicks() - data.getActionProcessor().getLastPlaceTick() < 10),
48 |
49 | NEAR_VEHICLE(data -> data.getPositionProcessor().isNearVehicle()),
50 |
51 | INSIDE_VEHICLE(data -> data.getPositionProcessor().getSinceVehicleTicks() < 20),
52 |
53 | LIQUID(data -> data.getPositionProcessor().isInLiquid()),
54 |
55 | UNDER_BLOCK(data -> data.getPositionProcessor().isBlockNearHead()),
56 |
57 | PISTON(data -> data.getPositionProcessor().getSinceNearPistonTicks() < 50),
58 |
59 | VOID(data -> data.getPlayer().getLocation().getY() < 4),
60 |
61 | COMBAT(data -> data.getCombatProcessor().getHitTicks() < 5),
62 |
63 | FLYING(data -> data.getPositionProcessor().getSinceFlyingTicks() < 40),
64 |
65 | AUTO_CLICKER(data -> data.getExemptProcessor().isExempt(ExemptType.PLACING, ExemptType.DIGGING, ExemptType.BLOCK_BREAK)),
66 |
67 | DEPTH_STRIDER(data -> PlayerUtil.getDepthStriderLevel(data.getPlayer()) > 0);
68 |
69 | private final Function exception;
70 |
71 | ExemptType(final Function exception) {
72 | this.exception = exception;
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/listener/BukkitEventListener.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.listener;
2 |
3 | import ai.medusa.anticheat.Medusa;
4 | import ai.medusa.anticheat.data.PlayerData;
5 | import org.bukkit.event.EventHandler;
6 | import org.bukkit.event.Listener;
7 | import org.bukkit.event.block.BlockBreakEvent;
8 | import org.bukkit.event.block.BlockPlaceEvent;
9 | import org.bukkit.event.player.PlayerInteractEvent;
10 |
11 | public final class BukkitEventListener implements Listener {
12 |
13 | @EventHandler
14 | public void onBlockBreak(final BlockBreakEvent event) {
15 | final PlayerData data = Medusa.INSTANCE.getPlayerDataManager().getPlayerData(event.getPlayer());
16 | if (data != null) {
17 | data.getActionProcessor().handleBukkitBlockBreak();
18 | }
19 | }
20 |
21 | @EventHandler
22 | public void onPlayerInteract(final PlayerInteractEvent event) {
23 | final PlayerData data = Medusa.INSTANCE.getPlayerDataManager().getPlayerData(event.getPlayer());
24 | if (data != null) {
25 | data.getActionProcessor().handleInteract(event);
26 | }
27 | }
28 |
29 | @EventHandler
30 | public void onBlockPlace(final BlockPlaceEvent event) {
31 | final PlayerData data = Medusa.INSTANCE.getPlayerDataManager().getPlayerData(event.getPlayer());
32 | if (data != null) {
33 | data.getActionProcessor().handleBukkitPlace();
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/listener/ClientBrandListener.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.listener;
2 |
3 | import ai.medusa.anticheat.Medusa;
4 | import ai.medusa.anticheat.data.PlayerData;
5 | import org.bukkit.entity.Player;
6 | import org.bukkit.event.EventHandler;
7 | import org.bukkit.event.Listener;
8 | import org.bukkit.event.player.PlayerJoinEvent;
9 | import org.bukkit.plugin.messaging.PluginMessageListener;
10 |
11 | import java.io.UnsupportedEncodingException;
12 | import java.lang.reflect.InvocationTargetException;
13 |
14 | public final class ClientBrandListener implements PluginMessageListener, Listener {
15 |
16 | @Override
17 | public void onPluginMessageReceived(final String channel, final Player player, final byte[] msg) {
18 | try {
19 | final PlayerData data = Medusa.INSTANCE.getPlayerDataManager().getPlayerData(player);
20 | if (data == null) return;
21 | data.setClientBrand(new String(msg, "UTF-8").substring(1));
22 | } catch (UnsupportedEncodingException e) {
23 | e.printStackTrace();
24 | }
25 | }
26 |
27 | @EventHandler
28 | public void onJoin(final PlayerJoinEvent event) {
29 | final Player player = event.getPlayer();
30 | addChannel(player, "MC|BRAND");
31 | }
32 |
33 | private void addChannel(final Player player, final String channel) {
34 | try {
35 | player.getClass().getMethod("addChannel", String.class).invoke(player, channel);
36 | } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException
37 | | SecurityException e) {
38 | e.printStackTrace();
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/listener/JoinQuitListener.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.listener;
2 |
3 | import ai.medusa.anticheat.Medusa;
4 | import ai.medusa.anticheat.util.anticheat.AlertUtil;
5 | import org.bukkit.event.EventHandler;
6 | import org.bukkit.event.Listener;
7 | import org.bukkit.event.player.PlayerJoinEvent;
8 | import org.bukkit.event.player.PlayerQuitEvent;
9 |
10 | public final class JoinQuitListener implements Listener {
11 |
12 | @EventHandler
13 | public void onPlayerJoin(final PlayerJoinEvent event) {
14 | Medusa.INSTANCE.getPlayerDataManager().add(event.getPlayer());
15 |
16 | if (event.getPlayer().hasPermission("medusa.alerts")) {
17 | AlertUtil.toggleAlerts(Medusa.INSTANCE.getPlayerDataManager().getPlayerData(event.getPlayer()));
18 | }
19 | }
20 |
21 | @EventHandler
22 | public void onPlayerQuit(final PlayerQuitEvent event) {
23 | Medusa.INSTANCE.getPlayerDataManager().remove(event.getPlayer());
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/listener/NetworkListener.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.listener;
2 |
3 | import ai.medusa.anticheat.Medusa;
4 | import ai.medusa.anticheat.data.PlayerData;
5 |
6 | import io.github.retrooper.packetevents.event.PacketListenerDynamic;
7 | import ai.medusa.anticheat.packet.Packet;
8 | import io.github.retrooper.packetevents.event.impl.PacketPlayReceiveEvent;
9 | import io.github.retrooper.packetevents.event.impl.PacketPlaySendEvent;
10 |
11 |
12 | public final class NetworkListener extends PacketListenerDynamic {
13 |
14 | @Override
15 | public void onPacketPlayReceive(final PacketPlayReceiveEvent event) {
16 | final PlayerData data = Medusa.INSTANCE.getPlayerDataManager().getPlayerData(event.getPlayer());
17 |
18 | handle: {
19 | if (data == null) break handle;
20 |
21 | Medusa.INSTANCE.getPacketExecutor().execute(() -> Medusa.INSTANCE.getReceivingPacketProcessor()
22 | .handle(data, new Packet(Packet.Direction.RECEIVE, event.getNMSPacket(), event.getPacketId())));
23 | }
24 | }
25 |
26 | @Override
27 | public void onPacketPlaySend(final PacketPlaySendEvent event) {
28 | final PlayerData data = Medusa.INSTANCE.getPlayerDataManager().getPlayerData(event.getPlayer());
29 |
30 | handle: {
31 | if (data == null) break handle;
32 |
33 | Medusa.INSTANCE.getPacketExecutor().execute(() -> Medusa.INSTANCE.getSendingPacketProcessor()
34 | .handle(data, new Packet(Packet.Direction.SEND, event.getNMSPacket(), event.getPacketId())));
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/manager/PlayerDataManager.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.manager;
2 |
3 | import ai.medusa.anticheat.data.PlayerData;
4 | import org.bukkit.entity.Player;
5 |
6 | import java.util.Collection;
7 | import java.util.Map;
8 | import java.util.UUID;
9 | import java.util.concurrent.ConcurrentHashMap;
10 |
11 | public final class PlayerDataManager {
12 |
13 | private final Map playerDataMap = new ConcurrentHashMap<>();
14 |
15 | public PlayerData getPlayerData(final Player player) {
16 | return playerDataMap.get(player.getUniqueId());
17 | }
18 |
19 | public void add(final Player player) {
20 | playerDataMap.put(player.getUniqueId(), new PlayerData(player));
21 | }
22 |
23 | public boolean has(final Player player) {
24 | return this.playerDataMap.containsKey(player.getUniqueId());
25 | }
26 |
27 | public void remove(final Player player) {
28 | playerDataMap.remove(player.getUniqueId());
29 | }
30 |
31 | public Collection getAllData() {
32 | return playerDataMap.values();
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/manager/ThemeManager.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.manager;
2 |
3 | import ai.medusa.anticheat.Medusa;
4 | import ai.medusa.anticheat.config.Config;
5 | import lombok.AllArgsConstructor;
6 | import lombok.Getter;
7 | import org.bukkit.ChatColor;
8 |
9 | import java.util.ArrayList;
10 | import java.util.List;
11 |
12 | public final class ThemeManager {
13 |
14 | public static final List themes = new ArrayList<>();
15 |
16 | public static void setup() {
17 | for (String str : Config.THEMES) {
18 | final String message = Medusa.INSTANCE.getPlugin().getConfig().getString("appearance.themes." + str + ".message");
19 | final List colors = Medusa.INSTANCE.getPlugin().getConfig().getStringList("appearance.themes." + str + ".colors");
20 |
21 | final List accentColourList = new ArrayList<>();
22 |
23 | for (String string : colors) {
24 | accentColourList.add(ChatColor.getByChar(string.charAt(1)));
25 | }
26 |
27 | final Theme theme = new Theme(str, message, accentColourList);
28 | themes.add(theme);
29 | }
30 | }
31 |
32 | @Getter
33 | @AllArgsConstructor
34 | public static class Theme {
35 | private final String name;
36 | private final String alertFormat;
37 | private final List accentColours;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/manager/TickManager.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.manager;
2 |
3 | import ai.medusa.anticheat.Medusa;
4 | import ai.medusa.anticheat.util.type.Pair;
5 | import lombok.Getter;
6 | import org.bukkit.Bukkit;
7 | import org.bukkit.Location;
8 | import org.bukkit.entity.Entity;
9 | import org.bukkit.scheduler.BukkitTask;
10 |
11 | public final class TickManager implements Runnable {
12 |
13 | @Getter
14 | private int ticks;
15 | private static BukkitTask task;
16 |
17 | public void start() {
18 | assert task == null : "TickProcessor has already been started!";
19 |
20 | task = Bukkit.getScheduler().runTaskTimer(Medusa.INSTANCE.getPlugin(), this, 0L, 1L);
21 | }
22 |
23 | public void stop() {
24 | if (task == null) return;
25 |
26 | task.cancel();
27 | task = null;
28 | }
29 |
30 | @Override
31 | public void run() {
32 | ticks++;
33 |
34 | Medusa.INSTANCE.getPlayerDataManager().getAllData().parallelStream()
35 | .forEach(data -> {
36 | final Entity target = data.getCombatProcessor().getTarget();
37 | final Entity lastTarget = data.getCombatProcessor().getLastTarget();
38 | if(target != null && lastTarget != null) {
39 | if (target != lastTarget) {
40 | data.getTargetLocations().clear();
41 | }
42 | Location location = target.getLocation();
43 | data.getTargetLocations().add(new Pair<>(location, ticks));
44 | }
45 | });
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/packet/Packet.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.packet;
2 |
3 | import io.github.retrooper.packetevents.packettype.PacketType;
4 | import io.github.retrooper.packetevents.packetwrappers.NMSPacket;
5 | import lombok.Getter;
6 |
7 | @Getter
8 | public final class Packet {
9 |
10 | private final Direction direction;
11 | private final NMSPacket rawPacket;
12 | private final byte packetId;
13 |
14 | public Packet(Direction direction, NMSPacket rawPacket, byte packetId) {
15 | this.direction = direction;
16 | this.rawPacket = rawPacket;
17 | this.packetId = packetId;
18 | }
19 |
20 | public boolean isReceiving() {
21 | return direction == Direction.RECEIVE;
22 | }
23 |
24 | public boolean isSending() {
25 | return direction == Direction.SEND;
26 | }
27 |
28 | public boolean isFlying() {
29 | return isReceiving() && PacketType.Play.Client.Util.isInstanceOfFlying(packetId);
30 | }
31 |
32 | public boolean isUseEntity() {
33 | return isReceiving() && packetId == PacketType.Play.Client.USE_ENTITY;
34 | }
35 |
36 | public boolean isExplosion() {
37 | return isSending() && packetId == PacketType.Play.Server.EXPLOSION;
38 | }
39 |
40 | public boolean isRotation() {
41 | return isReceiving() && (packetId == PacketType.Play.Client.LOOK || packetId == PacketType.Play.Client.POSITION_LOOK);
42 | }
43 |
44 | public boolean isPosition() {
45 | return isReceiving() && (packetId == PacketType.Play.Client.POSITION || packetId == PacketType.Play.Client.POSITION_LOOK);
46 | }
47 |
48 | public boolean isArmAnimation() {
49 | return isReceiving() && packetId == PacketType.Play.Client.ARM_ANIMATION;
50 | }
51 |
52 | public boolean isAbilities() {
53 | return isReceiving() && packetId == PacketType.Play.Client.ABILITIES;
54 | }
55 |
56 | public boolean isBlockPlace() {
57 | return isReceiving() && packetId == PacketType.Play.Client.BLOCK_PLACE;
58 | }
59 |
60 | public boolean isBlockDig() {
61 | return isReceiving() && packetId == PacketType.Play.Client.BLOCK_DIG;
62 | }
63 |
64 | public boolean isWindowClick() { return isReceiving() && packetId == PacketType.Play.Client.WINDOW_CLICK; }
65 |
66 | public boolean isEntityAction() {
67 | return isReceiving() && packetId == PacketType.Play.Client.ENTITY_ACTION;
68 | }
69 |
70 | public boolean isPosLook() {
71 | return isReceiving() && packetId == PacketType.Play.Client.POSITION_LOOK;
72 | }
73 |
74 | public boolean isCloseWindow() { return isReceiving() && packetId == PacketType.Play.Client.CLOSE_WINDOW; }
75 |
76 | public boolean isKeepAlive() { return isReceiving() && packetId == PacketType.Play.Client.KEEP_ALIVE; }
77 |
78 | public boolean isSteerVehicle() {
79 | return isReceiving() && packetId == PacketType.Play.Client.STEER_VEHICLE;
80 | }
81 |
82 | public boolean isHeldItemSlot() {
83 | return isReceiving() && packetId == PacketType.Play.Client.HELD_ITEM_SLOT;
84 | }
85 |
86 | public boolean isClientCommand() {
87 | return isReceiving() && packetId == PacketType.Play.Client.CLIENT_COMMAND;
88 | }
89 |
90 | public boolean isCustomPayload() { return isReceiving() && packetId == PacketType.Play.Client.CUSTOM_PAYLOAD; }
91 |
92 | public boolean isIncomingTransaction () {
93 | return isReceiving() && packetId == PacketType.Play.Client.TRANSACTION;
94 | }
95 |
96 | public boolean isSendingTransaction() {
97 | return isSending() && packetId == PacketType.Play.Server.TRANSACTION;
98 | }
99 |
100 | public boolean isAcceptingTeleport() {
101 | return isReceiving() && packetId == PacketType.Play.Client.TELEPORT_ACCEPT;
102 | }
103 | public boolean isTeleport() {
104 | return isSending() && packetId == PacketType.Play.Server.ENTITY_TELEPORT;
105 | }
106 |
107 | public boolean isOutPosition() {
108 | return isSending() && packetId == PacketType.Play.Server.POSITION;
109 | }
110 |
111 | public boolean isVelocity() {
112 | return isSending() && packetId == PacketType.Play.Server.ENTITY_VELOCITY;
113 | }
114 |
115 | public boolean isRelEntityMove() {
116 | return isSending() && packetId == PacketType.Play.Server.REL_ENTITY_MOVE;
117 | }
118 |
119 | public enum Direction { SEND, RECEIVE }
120 | }
121 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/packet/processor/ReceivingPacketProcessor.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.packet.processor;
2 |
3 | import ai.medusa.anticheat.data.PlayerData;
4 | import io.github.retrooper.packetevents.packetwrappers.play.in.blockdig.WrappedPacketInBlockDig;
5 | import io.github.retrooper.packetevents.packetwrappers.play.in.clientcommand.WrappedPacketInClientCommand;
6 | import io.github.retrooper.packetevents.packetwrappers.play.in.entityaction.WrappedPacketInEntityAction;
7 | import io.github.retrooper.packetevents.packetwrappers.play.in.flying.WrappedPacketInFlying;
8 | import io.github.retrooper.packetevents.packetwrappers.play.in.helditemslot.WrappedPacketInHeldItemSlot;
9 | import io.github.retrooper.packetevents.packetwrappers.play.in.transaction.WrappedPacketInTransaction;
10 | import io.github.retrooper.packetevents.packetwrappers.play.in.useentity.WrappedPacketInUseEntity;
11 | import ai.medusa.anticheat.packet.Packet;
12 |
13 | public final class ReceivingPacketProcessor {
14 |
15 | public void handle(final PlayerData data, final Packet packet) {
16 | if (packet.isEntityAction()) {
17 | final WrappedPacketInEntityAction wrapper = new WrappedPacketInEntityAction(packet.getRawPacket());
18 |
19 | data.getActionProcessor().handleEntityAction(wrapper);
20 | } else if (packet.isBlockDig()) {
21 | final WrappedPacketInBlockDig wrapper = new WrappedPacketInBlockDig(packet.getRawPacket());
22 |
23 | data.getActionProcessor().handleBlockDig(wrapper);
24 | } else if (packet.isClientCommand()) {
25 | final WrappedPacketInClientCommand wrapper = new WrappedPacketInClientCommand(packet.getRawPacket());
26 |
27 | data.getActionProcessor().handleClientCommand(wrapper);
28 | } else if (packet.isBlockPlace()) {
29 | data.getActionProcessor().handleBlockPlace();
30 | } else if (packet.isHeldItemSlot()) {
31 | final WrappedPacketInHeldItemSlot wrapper = new WrappedPacketInHeldItemSlot(packet.getRawPacket());
32 | data.getActionProcessor().handleHeldItemSlot(wrapper);
33 | } else if (packet.isCloseWindow()) {
34 | data.getActionProcessor().handleCloseWindow();
35 | } else if (packet.isUseEntity()) {
36 | final WrappedPacketInUseEntity wrapper = new WrappedPacketInUseEntity(packet.getRawPacket());
37 | data.getCombatProcessor().handleUseEntity(wrapper);
38 | } else if (packet.isFlying()) {
39 | final WrappedPacketInFlying wrapper = new WrappedPacketInFlying(packet.getRawPacket());
40 |
41 | data.getActionProcessor().handleFlying();
42 | data.getVelocityProcessor().handleFlying();
43 | data.getCombatProcessor().handleFlying();
44 |
45 | if (wrapper.isPosition()) {
46 | data.getPositionProcessor().handle(wrapper.getX(), wrapper.getY(), wrapper.getZ(), wrapper.isOnGround());
47 | }
48 | if (wrapper.isLook()) {
49 | data.getRotationProcessor().handle(wrapper.getYaw(), wrapper.getPitch());
50 | }
51 | } else if (packet.isArmAnimation()) {
52 | data.getClickProcessor().handleArmAnimation();
53 | data.getActionProcessor().handleArmAnimation();
54 | data.getCombatProcessor().handleArmAnimation();
55 | } else if (packet.isIncomingTransaction()) {
56 | final WrappedPacketInTransaction wrapper = new WrappedPacketInTransaction(packet.getRawPacket());
57 | data.getVelocityProcessor().handleTransaction(wrapper);
58 | }
59 | if (!data.getPlayer().hasPermission("medusa.bypass") || data.getPlayer().isOp()) {
60 | data.getChecks().forEach(check -> check.handle(packet));
61 | }
62 | }
63 | }
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/packet/processor/SendingPacketProcessor.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.packet.processor;
2 |
3 | import ai.medusa.anticheat.data.PlayerData;
4 | import io.github.retrooper.packetevents.packetwrappers.play.out.entityvelocity.WrappedPacketOutEntityVelocity;
5 | import ai.medusa.anticheat.packet.Packet;
6 | import io.github.retrooper.packetevents.packetwrappers.play.out.explosion.WrappedPacketOutExplosion;
7 | import io.github.retrooper.packetevents.packetwrappers.play.out.position.WrappedPacketOutPosition;
8 |
9 | public final class SendingPacketProcessor {
10 |
11 | public void handle(final PlayerData data, final Packet packet) {
12 | if (packet.isVelocity()) {
13 | final WrappedPacketOutEntityVelocity wrapper = new WrappedPacketOutEntityVelocity(packet.getRawPacket());
14 | if (wrapper.getEntity() == data.getPlayer()) {
15 | data.getVelocityProcessor().handle(wrapper.getVelocityX(), wrapper.getVelocityY(), wrapper.getVelocityZ());
16 | }
17 | }
18 | if (packet.isExplosion()) {
19 | final WrappedPacketOutExplosion wrapper = new WrappedPacketOutExplosion(packet.getRawPacket());
20 | data.getVelocityProcessor().handle(wrapper.getPlayerMotionX(), wrapper.getPlayerMotionY(), wrapper.getPlayerMotionZ());
21 | }
22 | if (packet.isOutPosition()) {
23 | final WrappedPacketOutPosition wrapper = new WrappedPacketOutPosition(packet.getRawPacket());
24 | data.getPositionProcessor().handleServerPosition(wrapper);
25 | }
26 | if (!data.getPlayer().hasPermission("medusa.bypass") || data.getPlayer().isOp()) {
27 | data.getChecks().forEach(check -> check.handle(packet));
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/util/ColorUtil.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.util;
2 |
3 | import lombok.experimental.UtilityClass;
4 | import org.bukkit.ChatColor;
5 |
6 | @UtilityClass
7 | public final class ColorUtil {
8 |
9 | public String translate(final String string) {
10 | return ChatColor.translateAlternateColorCodes('&', string);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/util/HitboxExpansion.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.util;
2 |
3 | import lombok.Getter;
4 | import org.bukkit.entity.Entity;
5 |
6 | /**
7 | * Created on 12/5/2020 Package ai.medusa.anticheat.util by infinitesm
8 | *
9 | * This is an enum of entity hitbox expansions, found from
10 | * https://technical-minecraft.fandom.com/wiki/Entity_Hitbox_Sizes.
11 | *
12 | * Names of the entities were found in EntityType.class.
13 | *
14 | * This enum is not an accurate representation of hitbox expansions for each entity,
15 | * if you need something that's accurate, look elsewhere. This is somewhat accurate and
16 | * helps to prevent Hitbox (A) from falsing due to entities with large hitbox expansions.
17 | */
18 |
19 | @Getter
20 | public enum HitboxExpansion {
21 |
22 | PLAYER("Player", 0.4),
23 | CREEPER("Creeper", 0.4),
24 | SKELETON("Skeleton", 0.4),
25 | SPIDER("Spider", 0.8),
26 | GIANT("Giant", 2.5),
27 | ZOMBIE("Zombie", 0.4),
28 | SLIME("Slime", 1.12), //Works for large slimes only for now.
29 | GHAST("Ghast", 2.1),
30 | PIG_ZOMBIE("PigZombie", 0.4),
31 | ENDERMAN("Enderman", 0.4),
32 | CAVE_SPIDER("CaveSpider", 0.45),
33 | SILVERFISH("Silverfish", 0.3),
34 | BLAZE("Blaze", 0.4),
35 | MAGMA_CUBE("LavaSlime", 1.12),
36 | ENDER_DRAGON("EnderDragon", 8.1),
37 | WITHER("WitherBoss", 0.55),
38 | BAT("Bat", 0.35),
39 | WITCH("Witch", 0.4),
40 | ENDERMITE("Endermite", 0.3),
41 | GUARDIAN("Guardian",0.525),
42 | PIG("Pig", 0.55), //Works for both baby and grown pigs.
43 | SHEEP("Sheep", 0.55),
44 | COW("Cow", 0.55),
45 | CHICKEN("Chicken", 0.3), //Works for both baby and grown chickens.
46 | SQUID("Squid", 0.5),
47 | WOLF("Wolf", 0.5), //Works for both baby and grown wolves.
48 | MUSHROOM_COW("MushroomCow", 0.55),
49 | SNOWMAN("SnowMan", 0.45),
50 | OCELOT("Ozelot", 0.4),
51 | IRON_GOLEM("VillagerGolem", 0.8),
52 | HORSE("EntityHorse", 0.7982),
53 | RABBIT("Rabbit", 0.3), //Works for both baby and grown rabbits.
54 | VILLAGER("Villager", 0.4);
55 |
56 | private final double expansion;
57 | private final String name;
58 |
59 | HitboxExpansion(final String name, final double expansion) {
60 | this.name = name;
61 | this.expansion = expansion;
62 | }
63 |
64 | public static double getExpansion(final Entity entity) {
65 | for (HitboxExpansion hitboxExpansion : values()) {
66 | final String name = entity.getType().getName();
67 | if (name != null) {
68 | if (name.equalsIgnoreCase(hitboxExpansion.getName())) {
69 | return hitboxExpansion.getExpansion();
70 | }
71 | }
72 | }
73 | return 0.4D;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/util/ReflectionUtil.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.util;
2 |
3 | import lombok.experimental.UtilityClass;
4 | import org.bukkit.Bukkit;
5 | import org.bukkit.entity.Entity;
6 | import org.bukkit.util.Vector;
7 | import ai.medusa.anticheat.util.type.BoundingBox;
8 | import java.lang.reflect.Field;
9 | import java.lang.reflect.InvocationTargetException;
10 | import java.lang.reflect.Method;
11 |
12 | @UtilityClass
13 | public final class ReflectionUtil {
14 |
15 | /**
16 | * We can make a caching system for this in order to be somewhat more efficient.
17 | * Reflection tends to be heavy, especially if we're using it every tick.
18 | * Maps would be great!
19 | */
20 |
21 | private String versionString;
22 |
23 | public Field getField(Class> clazz, String fieldName) {
24 | try {
25 | return clazz.getField(fieldName);
26 | } catch (NoSuchFieldException e) {
27 | e.printStackTrace();
28 | }
29 | return null;
30 | }
31 |
32 | public Method getMethod(Class> clazz, String methodName, Class>... params) {
33 | try {
34 | return clazz.getMethod(methodName, params);
35 | } catch (NoSuchMethodException e) {
36 | e.printStackTrace();
37 | }
38 | return null;
39 | }
40 |
41 | public String getVersion() {
42 | if (versionString == null) {
43 | String name = Bukkit.getServer().getClass().getPackage().getName();
44 | versionString = name.substring(name.lastIndexOf('.') + 1) + ".";
45 | }
46 |
47 | return versionString;
48 | }
49 |
50 | public Class> getNMSClass(String nmsClassName) {
51 | final String clazzName = "net.minecraft.server." + getVersion() + nmsClassName;
52 | try {
53 | return Class.forName(clazzName);
54 | } catch (Throwable t) {
55 | t.printStackTrace();
56 | }
57 |
58 | return null;
59 | }
60 |
61 | public Class> getOBCClass(String obcClassName) {
62 | final String clazzName = "org.bukkit.craftbukkit." + getVersion() + obcClassName;
63 | try {
64 | return Class.forName(clazzName);
65 | } catch (Throwable t) {
66 | t.printStackTrace();
67 | }
68 |
69 | return null;
70 | }
71 |
72 | public static Object craftEntity(Entity entity) {
73 | try {
74 | return getMethod(getOBCClass("entity.CraftEntity"), "getHandle").invoke(entity);
75 | } catch (IllegalAccessException | InvocationTargetException e) {
76 | e.printStackTrace();
77 | }
78 |
79 | return null;
80 | }
81 |
82 | public BoundingBox getBoundingBox(Entity entity) {
83 | try {
84 | final Object nmsBoundingBox = getMethod(getNMSClass("Entity"), "getBoundingBox")
85 | .invoke(craftEntity(entity));
86 |
87 | return toBoundingBox(nmsBoundingBox);
88 | } catch (IllegalAccessException | InvocationTargetException e) {
89 | e.printStackTrace();
90 | }
91 |
92 | return null;
93 | }
94 |
95 | public BoundingBox toBoundingBox(Object aaBB) {
96 |
97 | final Vector min = getBoxMin(aaBB);
98 | final Vector max = getBoxMax(aaBB);
99 |
100 | return new BoundingBox(min, max);
101 | }
102 |
103 | private Vector getBoxMin(Object box) {
104 |
105 | double x = 0D;
106 | double y = 0D;
107 | double z = 0D;
108 |
109 | Class> boxClass = box.getClass();
110 |
111 | try {
112 | if (!ServerUtil.isHigherThan1_13_2()) {
113 | x = (double) getField(boxClass, "a").get(box);
114 | y = (double) getField(boxClass, "b").get(box);
115 | z = (double) getField(boxClass, "c").get(box);
116 | } else {
117 | x = (double) getField(boxClass, "minX").get(box);
118 | y = (double) getField(boxClass, "minY").get(box);
119 | z = (double) getField(boxClass, "minZ").get(box);
120 | }
121 | } catch (IllegalAccessException e) {
122 | e.printStackTrace();
123 | }
124 |
125 | return new Vector(x, y, z);
126 | }
127 |
128 | private Vector getBoxMax(Object box) {
129 |
130 | double x = 0D;
131 | double y = 0D;
132 | double z = 0D;
133 |
134 | Class> boxClass = box.getClass();
135 |
136 | try {
137 | if (!ServerUtil.isHigherThan1_13_2()) {
138 | x = (double) getField(boxClass, "d").get(box);
139 | y = (double) getField(boxClass, "e").get(box);
140 | z = (double) getField(boxClass, "f").get(box);
141 | } else {
142 | x = (double) getField(boxClass, "maxX").get(box);
143 | y = (double) getField(boxClass, "maxY").get(box);
144 | z = (double) getField(boxClass, "maxZ").get(box);
145 | }
146 | } catch (IllegalAccessException e) {
147 | e.printStackTrace();
148 | }
149 |
150 | return new Vector(x, y, z);
151 | }
152 | }
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/util/ServerUtil.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.util;
2 |
3 | import io.github.retrooper.packetevents.PacketEvents;
4 | import io.github.retrooper.packetevents.utils.server.ServerVersion;
5 | import lombok.experimental.UtilityClass;
6 |
7 | @UtilityClass
8 | public final class ServerUtil {
9 |
10 | public double getTPS() {
11 | return PacketEvents.get().getServerUtils().getTPS();
12 | }
13 |
14 | public ServerVersion getServerVersion() {
15 | return PacketEvents.get().getServerUtils().getVersion();
16 | }
17 |
18 | public boolean isLowerThan1_8() {
19 | return getServerVersion().isLowerThan(ServerVersion.v_1_8);
20 | }
21 |
22 | public boolean isHigherThan1_13_2() {
23 | return getServerVersion().isHigherThan(ServerVersion.v_1_13_2);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/util/anticheat/AlertUtil.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.util.anticheat;
2 |
3 | import ai.medusa.api.listener.MedusaSendAlertEvent;
4 | import ai.medusa.anticheat.check.Check;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import ai.medusa.anticheat.util.ColorUtil;
7 | import ai.medusa.anticheat.util.PlayerUtil;
8 | import ai.medusa.anticheat.util.ServerUtil;
9 | import io.github.retrooper.packetevents.PacketEvents;
10 | import lombok.Getter;
11 | import ai.medusa.anticheat.config.Config;
12 | import lombok.experimental.UtilityClass;
13 | import net.md_5.bungee.api.chat.ClickEvent;
14 | import net.md_5.bungee.api.chat.ComponentBuilder;
15 | import net.md_5.bungee.api.chat.HoverEvent;
16 | import net.md_5.bungee.api.chat.TextComponent;
17 | import org.bukkit.Bukkit;
18 |
19 | import java.text.DecimalFormat;
20 | import java.util.HashSet;
21 | import java.util.Set;
22 |
23 | @Getter
24 | @UtilityClass
25 | public final class AlertUtil {
26 |
27 | private final Set alerts = new HashSet<>();
28 |
29 | public ToggleAlertType toggleAlerts(final PlayerData data) {
30 | if (alerts.contains(data)) {
31 | alerts.remove(data);
32 | return ToggleAlertType.REMOVE;
33 | } else {
34 | alerts.add(data);
35 | return ToggleAlertType.ADD;
36 | }
37 | }
38 |
39 | public void handleAlert(final Check check, final PlayerData data, final String info) {
40 | final TextComponent alertMessage = new TextComponent(ColorUtil.translate(Config.ALERT_FORMAT)
41 | .replaceAll("%player%", data.getPlayer().getName())
42 | .replaceAll("%uuid%", data.getPlayer().getUniqueId().toString())
43 | .replaceAll("%checkName%", check.getJustTheName())
44 | .replaceAll("%ping%", Integer.toString(PlayerUtil.getPing(data.getPlayer())))
45 | .replaceAll("%checkType%", Character.toString(check.getType()))
46 | .replaceAll("%dev%", check.getCheckInfo().experimental() ? ColorUtil.translate("&7*") : "")
47 | .replaceAll("%vl%", Integer.toString(check.getVl()))
48 | .replaceAll("%maxvl%", Integer.toString(check.getMaxVl()))
49 | .replaceAll("%ping%", Integer.toString(PlayerUtil.getPing(data.getPlayer())))
50 | .replaceAll("%tps%", new DecimalFormat("##.##").format(ServerUtil.getTPS())));
51 |
52 | alertMessage.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/tp " + data.getPlayer().getName()));
53 | alertMessage.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder(ColorUtil.translate(
54 | Config.ACCENT_ONE + "Description: &7" + check.getCheckInfo().description() +
55 | "\n" + Config.ACCENT_ONE + "Info: &7" + info +
56 | "\n" + Config.ACCENT_ONE + "Ping: &7" + PlayerUtil.getPing(data.getPlayer()) +
57 | "\n" + Config.ACCENT_ONE + "TPS: &7" + String.format("%.2f", PacketEvents.get().getServerUtils().getTPS()) +
58 | "\n" + Config.ACCENT_TWO + "Click to teleport.")).create()));
59 |
60 | final MedusaSendAlertEvent event = new MedusaSendAlertEvent(alertMessage, data.getPlayer(), check, info);
61 | Bukkit.getPluginManager().callEvent(event);
62 |
63 | if (event.isCancelled()) return;
64 |
65 | alerts.forEach(data1 -> data1.getPlayer().spigot().sendMessage(alertMessage));
66 | }
67 |
68 | public enum ToggleAlertType {
69 | ADD, REMOVE;
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/util/anticheat/PunishUtil.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.util.anticheat;
2 |
3 | import ai.medusa.anticheat.Medusa;
4 | import ai.medusa.anticheat.check.Check;
5 | import ai.medusa.anticheat.data.PlayerData;
6 | import lombok.experimental.UtilityClass;
7 | import org.bukkit.Bukkit;
8 |
9 | @UtilityClass
10 | public final class PunishUtil {
11 |
12 | public void punish(final Check check, final PlayerData data) {
13 | if (!check.getPunishCommand().isEmpty()) {
14 | Bukkit.getScheduler().runTask(Medusa.INSTANCE.getPlugin(), () ->
15 | Bukkit.getServer().dispatchCommand(Bukkit.getServer().getConsoleSender(), check.getPunishCommand()
16 | .replaceAll("%player%", data.getPlayer().getName())
17 | .replaceAll("%checkName%", check.getJustTheName())
18 | .replaceAll("%checkType", String.valueOf(check.getType()))));
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/util/type/CustomLocation.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.util.type;
2 |
3 | import lombok.Getter;
4 | import lombok.Setter;
5 | import org.bukkit.Location;
6 | import org.bukkit.World;
7 | import org.bukkit.util.Vector;
8 |
9 | @Getter
10 | @Setter
11 | public final class CustomLocation {
12 |
13 | private World world;
14 | private double x, y, z;
15 | private float yaw, pitch;
16 | private boolean onGround;
17 | private long timeStamp;
18 |
19 | public CustomLocation(World world, double x, double y, double z, float yaw, float pitch, boolean onGround) {
20 | this.world = world;
21 | this.x = x;
22 | this.y = y;
23 | this.z = z;
24 | this.yaw = yaw;
25 | this.pitch = pitch;
26 | this.onGround = onGround;
27 | this.timeStamp = System.currentTimeMillis();
28 | }
29 |
30 | public Vector toVector() {
31 | return new Vector(x, y, z);
32 | }
33 |
34 | public CustomLocation offset(double x, double y, double z, float yaw, float pitch) {
35 | return new CustomLocation(world, this.x + x, this.y + y, this.z + z, this.yaw + yaw, this.pitch + pitch, this.onGround);
36 | }
37 |
38 | public CustomLocation clone() {
39 | return new CustomLocation(world, x, y, z, yaw, pitch, onGround);
40 | }
41 |
42 | public static CustomLocation fromBukkit(Location location) {
43 | return new CustomLocation(location.getWorld(), location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch(), false);
44 | }
45 |
46 | public Location toBukkit(){ return new Location(world, x, y, z); }
47 |
48 | public Vector getDirection() {
49 | Vector vector = new Vector();
50 | double rotX = this.getYaw();
51 | double rotY = this.getPitch();
52 | vector.setY(-Math.sin(Math.toRadians(rotY)));
53 | double xz = Math.cos(Math.toRadians(rotY));
54 | vector.setX(-xz * Math.sin(Math.toRadians(rotX)));
55 | vector.setZ(xz * Math.cos(Math.toRadians(rotX)));
56 | return vector;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/util/type/EvictingList.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.util.type;
2 |
3 | import lombok.Getter;
4 |
5 | import java.util.Collection;
6 | import java.util.LinkedList;
7 |
8 | public final class EvictingList extends LinkedList {
9 |
10 | @Getter
11 | private final int maxSize;
12 |
13 | public EvictingList(int maxSize) {
14 | this.maxSize = maxSize;
15 | }
16 |
17 | public EvictingList(Collection extends T> c, int maxSize) {
18 | super(c);
19 | this.maxSize = maxSize;
20 | }
21 |
22 | @Override
23 | public boolean add(T t) {
24 | if (size() >= getMaxSize()) removeFirst();
25 | return super.add(t);
26 | }
27 |
28 | public boolean isFull() {
29 | return size() >= getMaxSize();
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/util/type/Pair.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.util.type;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Data;
5 |
6 | @Data @AllArgsConstructor
7 | public final class Pair {
8 |
9 | private X x;
10 | private Y y;
11 | }
12 |
--------------------------------------------------------------------------------
/Impl/src/main/java/ai/medusa/anticheat/util/type/RayTrace.java:
--------------------------------------------------------------------------------
1 | package ai.medusa.anticheat.util.type;
2 |
3 | import org.bukkit.entity.Player;
4 | import org.bukkit.util.Vector;
5 |
6 | public final class RayTrace {
7 |
8 | private final Vector origin;
9 | private final Vector direction;
10 |
11 | public RayTrace(final Vector origin, final Vector direction) {
12 | this.origin = origin;
13 | this.direction = direction;
14 | }
15 |
16 | public RayTrace(final Player player) {
17 | this.origin = player.getEyeLocation().toVector();
18 | this.direction = player.getEyeLocation().getDirection();
19 | }
20 |
21 | public double origin(int i) {
22 | switch (i) {
23 | case 0:
24 | return origin.getX();
25 | case 1:
26 | return origin.getY();
27 | case 2:
28 | return origin.getZ();
29 | default:
30 | return 0;
31 | }
32 | }
33 |
34 | public double direction(int i) {
35 | switch (i) {
36 | case 0:
37 | return direction.getX();
38 | case 1:
39 | return direction.getY();
40 | case 2:
41 | return direction.getZ();
42 | default:
43 | return 0;
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Impl/src/main/resources/plugin.yml:
--------------------------------------------------------------------------------
1 | name: Medusa
2 | version: 1.5.3
3 | main: ai.medusa.anticheat.MedusaPlugin
4 | authors: [infinitesm]
5 | description: Medusa Anticheat
6 |
7 | commands:
8 | medusa:
9 | description: Commands for Medusa Anticheat.
10 | usage: /medusa
11 | aliases: [anticheat, ac]
12 | permission: medusa.commands
13 |
14 |
15 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Medusa
2 |
3 | Automatic server moderation system for Spigot 1.8-1.12 servers.
4 |
5 | Features:
6 | - Real time behaviour anomaly detection + mitigation through proven and accurate detection methods
7 | - Lightweight, little to no performance overhead
8 | - Theme system
9 | - Multiversion support (1.8-1.12)
10 |
11 | Download here: https://www.spigotmc.org/resources/medusa.83345/
12 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | ai.medusa
8 | Medusa
9 | pom
10 | 1.0
11 |
12 |
13 | Impl
14 | API
15 | ExampleAPI
16 |
17 |
18 |
19 | UTF-8
20 |
21 |
22 |
23 |
24 | spigot-repo
25 | https://hub.spigotmc.org/nexus/content/repositories/snapshots/
26 |
27 |
28 | jitpack.io
29 | https://jitpack.io
30 |
31 |
32 | funkemunky-releases
33 | https://nexus.funkemunky.cc/content/repositories/releases/
34 |
35 |
36 |
37 |
38 |
39 | org.projectlombok
40 | lombok
41 | 1.18.10
42 | provided
43 |
44 |
45 | org.github.spigot
46 | 1.8
47 | 1.8
48 | provided
49 |
50 |
51 | org.github.spigot
52 | 1.8.3
53 | 1.8.3
54 | provided
55 |
56 |
57 | org.github.spigot
58 | 1.8.8
59 | 1.8.8
60 | provided
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------