From 2130ff3753ee74a6d0273eadcd347181d4e51e50 Mon Sep 17 00:00:00 2001 From: Anthony M Date: Wed, 1 May 2024 22:42:39 +0300 Subject: [PATCH 1/7] Update DiscordManager & its yml files. --- .../grim/grimac/manager/DiscordManager.java | 119 ++++++++++++++++-- src/main/resources/discord/de.yml | 50 ++++++-- src/main/resources/discord/en.yml | 49 ++++++-- src/main/resources/discord/es.yml | 59 ++++++--- src/main/resources/discord/fr.yml | 49 ++++++-- src/main/resources/discord/it.yml | 49 ++++++-- src/main/resources/discord/pt.yml | 49 ++++++-- src/main/resources/discord/ru.yml | 51 ++++++-- src/main/resources/discord/zh.yml | 50 ++++++-- 9 files changed, 421 insertions(+), 104 deletions(-) diff --git a/src/main/java/ac/grim/grimac/manager/DiscordManager.java b/src/main/java/ac/grim/grimac/manager/DiscordManager.java index b5c734465f..45c18478c1 100644 --- a/src/main/java/ac/grim/grimac/manager/DiscordManager.java +++ b/src/main/java/ac/grim/grimac/manager/DiscordManager.java @@ -10,7 +10,7 @@ import java.awt.*; import java.time.Instant; -import java.util.ArrayList; +import java.util.*; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -19,6 +19,8 @@ public class DiscordManager implements Initable { private static WebhookClient client; private int embedColor; private String staticContent = ""; + // Custom fields - not ideal but it works + it's easy to configure + private List> customFields; public static final Pattern WEBHOOK_PATTERN = Pattern.compile("(?:https?://)?(?:\\w+\\.)?\\w+\\.\\w+/api(?:/v\\d+)?/webhooks/(\\d+)/([\\w-]+)(?:/(?:\\w+)?)?"); @@ -50,6 +52,11 @@ public void start() { sb.append(string).append("\n"); } staticContent = sb.toString(); + + // never tested what would happen if fields is empty + customFields = new ArrayList<>(); + List> fieldMaps = GrimAPI.INSTANCE.getConfigManager().getConfig().getListElse("webhook.fields", getDefaultFields()); + customFields.addAll(fieldMaps); } catch (Exception e) { e.printStackTrace(); } @@ -67,28 +74,120 @@ private List getDefaultContents() { return list; } + public List> getDefaultFields() { + List> fields = new ArrayList<>(); + + // Server Information field + Map serverInfoField = new HashMap<>(); + serverInfoField.put("name", "Server Information"); + serverInfoField.put("value", Arrays.asList( + "```properties", + "Server: %server%", + "TPS: %tps%", + "```" + )); + serverInfoField.put("inline", false); + fields.add(serverInfoField); + + // Client Information field + Map clientInfoField = new HashMap<>(); + clientInfoField.put("name", "Client Information"); + clientInfoField.put("value", Arrays.asList( + "```properties", + "Version: %version%", + "Brand: %brand%", + "```" + )); + clientInfoField.put("inline", false); + fields.add(clientInfoField); + + // Player Information field + Map playerInfoField = new HashMap<>(); + playerInfoField.put("name", "Player Information"); + playerInfoField.put("value", Arrays.asList( + "```properties", + "Player: %player%", + "UUID: %uuid%", + "Ping: %ping%", + "Horizontal Sensitivity: %h_sensitivity%%", + "Vertical Sensitivity: %v_sensitivity%%", + "Fast Math: %fast_math%", + "```" + )); + playerInfoField.put("inline", false); + fields.add(playerInfoField); + + // Check Information field + Map checkInfoField = new HashMap<>(); + checkInfoField.put("name", "Check Information"); + checkInfoField.put("value", Arrays.asList( + "```properties", + "Check: %check%", + "Violations: %violations%", + "```" + )); + checkInfoField.put("inline", false); + fields.add(checkInfoField); + + return fields; + } + public void sendAlert(GrimPlayer player, String verbose, String checkName, String violations) { if (client != null) { - String content = staticContent + ""; - content = content.replace("%check%", checkName); - content = content.replace("%violations%", violations); - content = GrimAPI.INSTANCE.getExternalAPI().replaceVariables(player, content, false); - content = content.replace("_", "\\_"); + String description = staticContent; + description = description.replace("%check%", checkName); + description = description.replace("%violations%", violations); + description = description.replace("%grim_version%", GrimAPI.INSTANCE.getPlugin().getDescription().getVersion()); + description = GrimAPI.INSTANCE.getExternalAPI().replaceVariables(player, description, false); + description = description.replaceAll("_", "\\_"); WebhookEmbedBuilder embed = new WebhookEmbedBuilder() .setImageUrl("https://i.stack.imgur.com/Fzh0w.png") // Constant width - .setThumbnailUrl("https://crafthead.net/helm/" + player.user.getProfile().getUUID()) .setColor(embedColor) .setTitle(new WebhookEmbed.EmbedTitle("**Grim Alert**", null)) - .setDescription(content) + .setDescription(description) .setTimestamp(Instant.now()) .setFooter(new WebhookEmbed.EmbedFooter("", "https://grim.ac/images/grim.png")); - if (!verbose.isEmpty()) { - embed.addField(new WebhookEmbed.EmbedField(true, "Verbose", verbose)); + if (GrimAPI.INSTANCE.getConfigManager().getConfig().getBooleanElse("webhook.show-player-head", true)) { + embed.setThumbnailUrl("https://crafthead.net/helm/" + player.user.getProfile().getUUID()); + } + + // Add custom fields if they exist + for (Map field : customFields) { + String name = (String) field.get("name"); + List value = (List) field.get("value"); + boolean inline = (boolean) field.getOrDefault("inline", false); + + // Replace placeholders in field values + List fieldValue = new ArrayList<>(); + for (String line : value) { + line = line.replace("%check%", checkName); // Replace %check% placeholder + line = line.replace("%violations%", violations); // Replace %violations% placeholder + line = line.replace("%grim_version%", GrimAPI.INSTANCE.getPlugin().getDescription().getVersion()); + line = GrimAPI.INSTANCE.getExternalAPI().replaceVariables(player, line, false); + line = line.replaceAll("_", "\\_"); + fieldValue.add(line); + } + + StringBuilder fieldValueString = new StringBuilder(); + for (String line : fieldValue) { + fieldValueString.append(line).append("\n"); + } + + embed.addField(new WebhookEmbed.EmbedField(inline, name, fieldValueString.toString())); } + if (!verbose.isEmpty()) { + embed.addField(new WebhookEmbed.EmbedField( + true, + "Verbose", + "```properties\n" + + verbose + + "```" + )); + } sendWebhookEmbed(embed); } } diff --git a/src/main/resources/discord/de.yml b/src/main/resources/discord/de.yml index bbbd5bbf47..1ebeca7a4e 100644 --- a/src/main/resources/discord/de.yml +++ b/src/main/resources/discord/de.yml @@ -1,12 +1,40 @@ -# Ob Discord Webhook aktiviert werden soll enabled: false -webhook: "" -embed-color: "#00FFFF" -violation-content: - - "**Spieler**: %player%" - - "**Check**: %check%" - - "**Verstöße**: %violations%" - - "**Client-Version**: %version%" - - "**Marke**: %brand%" - - "**Ping**: %ping%" - - "**TPS**: %tps%" \ No newline at end of file +webhook: + url: "" + embed-color: "#00FFFF" + show-player-head: true + description: + - "Grim Version: %grrim_version%" + fields: + - name: "Server Information" + value: + - "```properties" + - "Server: server_name" + - "TPS: %tps%" + - "```" + inline: false + - name: "Client Information" + value: + - "```properties" + - "Version: %version%" + - "Brand: %brand%" + - "```" + inline: false + - name: "Player Information" + value: + - "```properties" + - "Player: %player%" + - "UUID: %uuid%" + - "Ping: %ping%" + - "Horizontal Sensitivity: %h_sensitivity%%" + - "Vertical Sensitivity: %v_sensitivity%%" + - "Fast Math: %fast_math%" + - "```" + inline: false + - name: "Check Information" + value: + - "```properties" + - "Check: %check%" + - "Violations: %violations%" + - "```" + inline: false \ No newline at end of file diff --git a/src/main/resources/discord/en.yml b/src/main/resources/discord/en.yml index 3e8ec153fe..1ebeca7a4e 100644 --- a/src/main/resources/discord/en.yml +++ b/src/main/resources/discord/en.yml @@ -1,11 +1,40 @@ enabled: false -webhook: "" -embed-color: "#00FFFF" -violation-content: - - "**Player**: %player%" - - "**Check**: %check%" - - "**Violations**: %violations%" - - "**Client Version**: %version%" - - "**Brand**: %brand%" - - "**Ping**: %ping%" - - "**TPS**: %tps%" \ No newline at end of file +webhook: + url: "" + embed-color: "#00FFFF" + show-player-head: true + description: + - "Grim Version: %grrim_version%" + fields: + - name: "Server Information" + value: + - "```properties" + - "Server: server_name" + - "TPS: %tps%" + - "```" + inline: false + - name: "Client Information" + value: + - "```properties" + - "Version: %version%" + - "Brand: %brand%" + - "```" + inline: false + - name: "Player Information" + value: + - "```properties" + - "Player: %player%" + - "UUID: %uuid%" + - "Ping: %ping%" + - "Horizontal Sensitivity: %h_sensitivity%%" + - "Vertical Sensitivity: %v_sensitivity%%" + - "Fast Math: %fast_math%" + - "```" + inline: false + - name: "Check Information" + value: + - "```properties" + - "Check: %check%" + - "Violations: %violations%" + - "```" + inline: false \ No newline at end of file diff --git a/src/main/resources/discord/es.yml b/src/main/resources/discord/es.yml index e8bff28b17..1ebeca7a4e 100644 --- a/src/main/resources/discord/es.yml +++ b/src/main/resources/discord/es.yml @@ -1,21 +1,40 @@ -# Configuraciones del webhook de Discord -# Si tienes dudas en como usarlos, puedes consultar el artículo de soporte de Discord: https://support.discord.com/hc/es/articles/228383668 - -# ¿Deberíamos usar webhooks? enabled: false - -# La URL del webhook. -webhook: "" - -# El color del embed que se mandara por este webhook. -embed-color: "#00FFFF" - -# El contenido del embed que se mandara por este webhook. -violation-content: - - "**Jugador**: %player%" - - "**Comprobación**: %check%" - - "**Violaciones**: %violations%" - - "**Version del cliente**: %version%" - - "**Marca del cliente**: %brand%" - - "**Latencia**: %ping%" - - "**TPS**: %tps%" \ No newline at end of file +webhook: + url: "" + embed-color: "#00FFFF" + show-player-head: true + description: + - "Grim Version: %grrim_version%" + fields: + - name: "Server Information" + value: + - "```properties" + - "Server: server_name" + - "TPS: %tps%" + - "```" + inline: false + - name: "Client Information" + value: + - "```properties" + - "Version: %version%" + - "Brand: %brand%" + - "```" + inline: false + - name: "Player Information" + value: + - "```properties" + - "Player: %player%" + - "UUID: %uuid%" + - "Ping: %ping%" + - "Horizontal Sensitivity: %h_sensitivity%%" + - "Vertical Sensitivity: %v_sensitivity%%" + - "Fast Math: %fast_math%" + - "```" + inline: false + - name: "Check Information" + value: + - "```properties" + - "Check: %check%" + - "Violations: %violations%" + - "```" + inline: false \ No newline at end of file diff --git a/src/main/resources/discord/fr.yml b/src/main/resources/discord/fr.yml index 4eff964c8d..1ebeca7a4e 100644 --- a/src/main/resources/discord/fr.yml +++ b/src/main/resources/discord/fr.yml @@ -1,11 +1,40 @@ enabled: false -webhook: "" -embed-color: "#00FFFF" -violation-content: - - "**Joueur**: %player%" - - "**Vérification**: %check%" - - "**Violations**: %violations%" - - "**Version du client**: %version%" - - "**Nature du client**: %brand%" - - "**Latence**: %ping%" - - "**TPS**: %tps%" +webhook: + url: "" + embed-color: "#00FFFF" + show-player-head: true + description: + - "Grim Version: %grrim_version%" + fields: + - name: "Server Information" + value: + - "```properties" + - "Server: server_name" + - "TPS: %tps%" + - "```" + inline: false + - name: "Client Information" + value: + - "```properties" + - "Version: %version%" + - "Brand: %brand%" + - "```" + inline: false + - name: "Player Information" + value: + - "```properties" + - "Player: %player%" + - "UUID: %uuid%" + - "Ping: %ping%" + - "Horizontal Sensitivity: %h_sensitivity%%" + - "Vertical Sensitivity: %v_sensitivity%%" + - "Fast Math: %fast_math%" + - "```" + inline: false + - name: "Check Information" + value: + - "```properties" + - "Check: %check%" + - "Violations: %violations%" + - "```" + inline: false \ No newline at end of file diff --git a/src/main/resources/discord/it.yml b/src/main/resources/discord/it.yml index 682fcc6ea6..1ebeca7a4e 100644 --- a/src/main/resources/discord/it.yml +++ b/src/main/resources/discord/it.yml @@ -1,11 +1,40 @@ enabled: false -webhook: "" -embed-color: "#00FFFF" -violation-content: - - "**Giocatore**: %player%" - - "**Cheats Rilevati**: %check%" - - "**Violazioni**: %violations%" - - "**Versione Client**: %version%" - - "**Client**: %brand%" - - "**Ping**: %ping%" - - "**TPS**: %tps%" +webhook: + url: "" + embed-color: "#00FFFF" + show-player-head: true + description: + - "Grim Version: %grrim_version%" + fields: + - name: "Server Information" + value: + - "```properties" + - "Server: server_name" + - "TPS: %tps%" + - "```" + inline: false + - name: "Client Information" + value: + - "```properties" + - "Version: %version%" + - "Brand: %brand%" + - "```" + inline: false + - name: "Player Information" + value: + - "```properties" + - "Player: %player%" + - "UUID: %uuid%" + - "Ping: %ping%" + - "Horizontal Sensitivity: %h_sensitivity%%" + - "Vertical Sensitivity: %v_sensitivity%%" + - "Fast Math: %fast_math%" + - "```" + inline: false + - name: "Check Information" + value: + - "```properties" + - "Check: %check%" + - "Violations: %violations%" + - "```" + inline: false \ No newline at end of file diff --git a/src/main/resources/discord/pt.yml b/src/main/resources/discord/pt.yml index 7fe4458072..f29a693ab9 100644 --- a/src/main/resources/discord/pt.yml +++ b/src/main/resources/discord/pt.yml @@ -1,11 +1,40 @@ enabled: false -webhook: "" -embed-color: "#00FFFF" -violation-content: - - "**Jogador**: %player%" - - "**Verificação**: %check%" - - "**Violações**: %violations%" - - "**Versão do Cliente**: %version%" - - "**Nome do Cliente**: %brand%" - - "**Ping**: %ping%" - - "**TPS**: %tps%" \ No newline at end of file +webhook: + url: "" + embed-color: "#00FFFF" + show-player-head: true + description: + - "Grim Version: %grrim_version%" + fields: + - name: "Server Information" + value: + - "```properties" + - "Server: server_name" + - "TPS: %tps%" + - "```" + inline: false + - name: "Client Information" + value: + - "```properties" + - "Version: %version%" + - "Brand: %brand%" + - "```" + inline: false + - name: "Player Information" + value: + - "```properties" + - "Player: %player%" + - "UUID: %uuid%" + - "Ping: %ping%" + - "Horizontal Sensitivity: %h_sensitivity%%" + - "Vertical Sensitivity: %v_sensitivity%%" + - "Fast Math: %fast_math%" + - "```" + inline: false + - name: "Check Information" + value: + - "```properties" + - "Check: %check%" + - "Violations: %violations%" + - "```" + inline: false \ No newline at end of file diff --git a/src/main/resources/discord/ru.yml b/src/main/resources/discord/ru.yml index 0a195dfb99..1ebeca7a4e 100644 --- a/src/main/resources/discord/ru.yml +++ b/src/main/resources/discord/ru.yml @@ -1,13 +1,40 @@ -# Включать ли веб-крючок discord enabled: false -webhook: "" -embed-color: "#00FFFF" -violation-content: - - "**Игрок**: %player%" - - "**Проверка**: %check%" - - "**Нарушения**: %violations%" - - "**Версия Клиента**: %version%" - - "**Бренд**: %brand%" - - "**Пинг**: %ping%" - - "**ТПС**: %tps%" - \ No newline at end of file +webhook: + url: "" + embed-color: "#00FFFF" + show-player-head: true + description: + - "Grim Version: %grrim_version%" + fields: + - name: "Server Information" + value: + - "```properties" + - "Server: server_name" + - "TPS: %tps%" + - "```" + inline: false + - name: "Client Information" + value: + - "```properties" + - "Version: %version%" + - "Brand: %brand%" + - "```" + inline: false + - name: "Player Information" + value: + - "```properties" + - "Player: %player%" + - "UUID: %uuid%" + - "Ping: %ping%" + - "Horizontal Sensitivity: %h_sensitivity%%" + - "Vertical Sensitivity: %v_sensitivity%%" + - "Fast Math: %fast_math%" + - "```" + inline: false + - name: "Check Information" + value: + - "```properties" + - "Check: %check%" + - "Violations: %violations%" + - "```" + inline: false \ No newline at end of file diff --git a/src/main/resources/discord/zh.yml b/src/main/resources/discord/zh.yml index 9f26cb953a..f29a693ab9 100644 --- a/src/main/resources/discord/zh.yml +++ b/src/main/resources/discord/zh.yml @@ -1,12 +1,40 @@ -# 是否启用discord webhook enabled: false -webhook: "" -embed-color: "#00FFFF" -violation-content: - - "**Player**: %player%" - - "**Check**: %check%" - - "**Violations**: %violations%" - - "**Client Version**: %version%" - - "**Brand**: %brand%" - - "**Ping**: %ping%" - - "**TPS**: %tps%" +webhook: + url: "" + embed-color: "#00FFFF" + show-player-head: true + description: + - "Grim Version: %grrim_version%" + fields: + - name: "Server Information" + value: + - "```properties" + - "Server: server_name" + - "TPS: %tps%" + - "```" + inline: false + - name: "Client Information" + value: + - "```properties" + - "Version: %version%" + - "Brand: %brand%" + - "```" + inline: false + - name: "Player Information" + value: + - "```properties" + - "Player: %player%" + - "UUID: %uuid%" + - "Ping: %ping%" + - "Horizontal Sensitivity: %h_sensitivity%%" + - "Vertical Sensitivity: %v_sensitivity%%" + - "Fast Math: %fast_math%" + - "```" + inline: false + - name: "Check Information" + value: + - "```properties" + - "Check: %check%" + - "Violations: %violations%" + - "```" + inline: false \ No newline at end of file From 6edc729349da88f44eeb9b2ed987b824d8dc3470 Mon Sep 17 00:00:00 2001 From: Anthony M Date: Thu, 2 May 2024 16:27:11 +0300 Subject: [PATCH 2/7] i forgot to add .url after webhook --- src/main/java/ac/grim/grimac/manager/DiscordManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/ac/grim/grimac/manager/DiscordManager.java b/src/main/java/ac/grim/grimac/manager/DiscordManager.java index 45c18478c1..58c1d9792c 100644 --- a/src/main/java/ac/grim/grimac/manager/DiscordManager.java +++ b/src/main/java/ac/grim/grimac/manager/DiscordManager.java @@ -28,7 +28,7 @@ public class DiscordManager implements Initable { public void start() { try { if (!GrimAPI.INSTANCE.getConfigManager().getConfig().getBooleanElse("enabled", false)) return; - String webhook = GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("webhook", ""); + String webhook = GrimAPI.INSTANCE.getConfigManager().getConfig().getStringElse("webhook.url", ""); if (webhook.isEmpty()) { LogUtil.warn("Discord webhook is empty, disabling Discord alerts"); client = null; From 19417719cf09057a927c4b664b896ab18894a0ec Mon Sep 17 00:00:00 2001 From: Anthony M Date: Thu, 2 May 2024 16:52:33 +0300 Subject: [PATCH 3/7] fix "description" value --- .../java/ac/grim/grimac/manager/DiscordManager.java | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/main/java/ac/grim/grimac/manager/DiscordManager.java b/src/main/java/ac/grim/grimac/manager/DiscordManager.java index 58c1d9792c..79313355f0 100644 --- a/src/main/java/ac/grim/grimac/manager/DiscordManager.java +++ b/src/main/java/ac/grim/grimac/manager/DiscordManager.java @@ -48,7 +48,7 @@ public void start() { LogUtil.warn("Discord embed color is invalid"); } StringBuilder sb = new StringBuilder(); - for (String string : GrimAPI.INSTANCE.getConfigManager().getConfig().getStringListElse("violation-content", getDefaultContents())) { + for (String string : GrimAPI.INSTANCE.getConfigManager().getConfig().getStringListElse("webhook.description", getDefaultDescription())) { sb.append(string).append("\n"); } staticContent = sb.toString(); @@ -62,15 +62,9 @@ public void start() { } } - private List getDefaultContents() { + private List getDefaultDescription() { List list = new ArrayList<>(); - list.add("**Player**: %player%"); - list.add("**Check**: %check%"); - list.add("**Violations**: %violations%"); - list.add("**Client Version**: %version%"); - list.add("**Brand**: %brand%"); - list.add("**Ping**: %ping%"); - list.add("**TPS**: %tps%"); + list.add("No Cheat Enforcer Version: %nce_version%"); return list; } From 4dee33a45e1e7495f5879220b1f7c3f1a190388b Mon Sep 17 00:00:00 2001 From: "Anthony M." Date: Tue, 6 Aug 2024 21:22:02 +0300 Subject: [PATCH 4/7] ADD: ArmAnimation & related + AutoClickA --- .../checks/impl/autoclicker/AutoClickerA.java | 33 ++++++++++++ .../grimac/checks/type/ArmAnimationCheck.java | 11 ++++ .../events/packets/CheckManagerListener.java | 51 +++++++++++++++++++ .../ac/grim/grimac/manager/CheckManager.java | 14 +++++ .../anticheat/update/ArmAnimationUpdate.java | 15 ++++++ src/main/resources/config/en.yml | 5 ++ 6 files changed, 129 insertions(+) create mode 100644 src/main/java/ac/grim/grimac/checks/impl/autoclicker/AutoClickerA.java create mode 100644 src/main/java/ac/grim/grimac/checks/type/ArmAnimationCheck.java create mode 100644 src/main/java/ac/grim/grimac/utils/anticheat/update/ArmAnimationUpdate.java diff --git a/src/main/java/ac/grim/grimac/checks/impl/autoclicker/AutoClickerA.java b/src/main/java/ac/grim/grimac/checks/impl/autoclicker/AutoClickerA.java new file mode 100644 index 0000000000..a6e41e178b --- /dev/null +++ b/src/main/java/ac/grim/grimac/checks/impl/autoclicker/AutoClickerA.java @@ -0,0 +1,33 @@ +package ac.grim.grimac.checks.impl.autoclicker; + +import ac.grim.grimac.checks.Check; +import ac.grim.grimac.checks.CheckData; +import ac.grim.grimac.checks.type.ArmAnimationCheck; +import ac.grim.grimac.player.GrimPlayer; +import ac.grim.grimac.utils.anticheat.update.ArmAnimationUpdate; + +/* + This check is responsible for alerting everyone with grim.alerts when a player is clicking too fast. + This check is a simple example of how to use the ArmAnimationCheck interface. + You can even create your own heuristics to detect autoclickers. + */ +@CheckData(name = "AutoClickerA", alternativeName = "AutoclickerA") +public class AutoClickerA extends Check implements ArmAnimationCheck { + private int MAX_CPS; + + public AutoClickerA(final GrimPlayer player) { + super(player); + } + + @Override + public void process(final ArmAnimationUpdate armAnimationUpdate) { + final int cps = armAnimationUpdate.getLeftClicks(); + if (cps > MAX_CPS) alert("cps=" + cps + ", max=" + MAX_CPS); + } + + @Override + public void reload() { + super.reload(); + MAX_CPS = getConfig().getIntElse("AutoClicker.max_cps", 20); + } +} diff --git a/src/main/java/ac/grim/grimac/checks/type/ArmAnimationCheck.java b/src/main/java/ac/grim/grimac/checks/type/ArmAnimationCheck.java new file mode 100644 index 0000000000..ff0bd42c30 --- /dev/null +++ b/src/main/java/ac/grim/grimac/checks/type/ArmAnimationCheck.java @@ -0,0 +1,11 @@ +package ac.grim.grimac.checks.type; + +import ac.grim.grimac.api.AbstractCheck; +import ac.grim.grimac.utils.anticheat.update.ArmAnimationUpdate; + +public interface ArmAnimationCheck extends AbstractCheck { + + default void process(final ArmAnimationUpdate armAnimationUpdate) { + } + +} diff --git a/src/main/java/ac/grim/grimac/events/packets/CheckManagerListener.java b/src/main/java/ac/grim/grimac/events/packets/CheckManagerListener.java index 4cbe7827ec..d6f9b95c11 100644 --- a/src/main/java/ac/grim/grimac/events/packets/CheckManagerListener.java +++ b/src/main/java/ac/grim/grimac/events/packets/CheckManagerListener.java @@ -48,8 +48,10 @@ import io.github.retrooper.packetevents.util.SpigotConversionUtil; import org.bukkit.util.Vector; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.TimeUnit; import java.util.function.BiFunction; public class CheckManagerListener extends PacketListenerAbstract { @@ -58,6 +60,12 @@ public CheckManagerListener() { super(PacketListenerPriority.LOW); } + private static final long TIME_WINDOW = TimeUnit.SECONDS.toMillis(1), + ANIMATION_WINDOW = 200; // 200 milliseconds for right-click animation + private long lastCPSCheck = 0; + private final ArrayDeque leftClickTimestamps = new ArrayDeque<>(), + rightClickTimestamps = new ArrayDeque<>(); + // Copied from MCP... // Returns null if there isn't anything. // @@ -379,6 +387,9 @@ public void onPacketReceive(PacketReceiveEvent event) { if (event.getConnectionState() != ConnectionState.PLAY) return; GrimPlayer player = GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser()); if (player == null) return; + long currentTime = System.currentTimeMillis(); + + boolean digging = false; // Determine if teleport BEFORE we call the pre-prediction vehicle if (event.getPacketType() == PacketType.Play.Client.VEHICLE_MOVE) { @@ -461,6 +472,7 @@ public void onPacketReceive(PacketReceiveEvent event) { player.checkManager.getPacketCheck(BadPacketsZ.class).handle(event, dig); if (dig.getAction() == DiggingAction.FINISHED_DIGGING) { + digging = false; // Not unbreakable if (!block.getType().isAir() && block.getType().getHardness() != -1.0f && !event.isCancelled()) { player.compensatedWorld.startPredicting(); @@ -470,6 +482,7 @@ public void onPacketReceive(PacketReceiveEvent event) { } if (dig.getAction() == DiggingAction.START_DIGGING && !event.isCancelled()) { + digging = true; double damage = BlockBreakSpeed.getBlockDamage(player, dig.getBlockPosition()); //Instant breaking, no damage means it is unbreakable by creative players (with swords) @@ -486,8 +499,13 @@ public void onPacketReceive(PacketReceiveEvent event) { } } + if (dig.getAction() == DiggingAction.CANCELLED_DIGGING) { + digging = false; + } + if (!event.isCancelled()) { if (dig.getAction() == DiggingAction.START_DIGGING || dig.getAction() == DiggingAction.FINISHED_DIGGING || dig.getAction() == DiggingAction.CANCELLED_DIGGING) { + leftClickTimestamps.clear(); player.compensatedWorld.handleBlockBreakPrediction(dig); } } @@ -566,6 +584,21 @@ public void onPacketReceive(PacketReceiveEvent event) { player.lastBlockPlaceUseItem = System.currentTimeMillis(); } + // Handle left-clicks + if (event.getPacketType() == PacketType.Play.Client.ANIMATION) { + if (isRecentRightClick(currentTime)) return; + if (digging) return; + handleLeftClick(currentTime); + } + + if (currentTime - lastCPSCheck >= TIME_WINDOW) { + final ArmAnimationUpdate update = new ArmAnimationUpdate(leftClickTimestamps.size(), rightClickTimestamps.size()); + player.checkManager.onArmAnimation(update); + leftClickTimestamps.clear(); + rightClickTimestamps.clear(); + lastCPSCheck = currentTime; + } + // Call the packet checks last as they can modify the contents of the packet // Such as the NoFall check setting the player to not be on the ground player.checkManager.onPacketReceive(event); @@ -827,6 +860,24 @@ private static HitData getNearestHitResult(GrimPlayer player, StateType heldItem }); } + private boolean isRecentRightClick(final long currentTime) { + return !rightClickTimestamps.isEmpty() && currentTime - rightClickTimestamps.peekLast() <= ANIMATION_WINDOW; + } + + private void handleLeftClick(final long currentTime) { + leftClickTimestamps.addLast(currentTime); + removeOldTimestamps(currentTime, leftClickTimestamps); + } + + private void handleRightClick(final long currentTime) { + rightClickTimestamps.addLast(currentTime); + removeOldTimestamps(currentTime, rightClickTimestamps); + } + + private void removeOldTimestamps(final long currentTime, final ArrayDeque timestamps) { + while (!timestamps.isEmpty() && currentTime - timestamps.peek() > TIME_WINDOW) timestamps.pollFirst(); + } + @Override public void onPacketSend(PacketSendEvent event) { if (event.getConnectionState() != ConnectionState.PLAY) return; diff --git a/src/main/java/ac/grim/grimac/manager/CheckManager.java b/src/main/java/ac/grim/grimac/manager/CheckManager.java index 47451a29d7..7663bdade5 100644 --- a/src/main/java/ac/grim/grimac/manager/CheckManager.java +++ b/src/main/java/ac/grim/grimac/manager/CheckManager.java @@ -5,6 +5,7 @@ import ac.grim.grimac.checks.impl.aim.AimDuplicateLook; import ac.grim.grimac.checks.impl.aim.AimModulo360; import ac.grim.grimac.checks.impl.aim.processor.AimProcessor; +import ac.grim.grimac.checks.impl.autoclicker.AutoClickerA; import ac.grim.grimac.checks.impl.badpackets.*; import ac.grim.grimac.checks.impl.baritone.Baritone; import ac.grim.grimac.checks.impl.combat.Reach; @@ -44,6 +45,7 @@ import com.google.common.collect.ImmutableClassToInstanceMap; public class CheckManager { + ClassToInstanceMap armAnimationCheck; ClassToInstanceMap packetChecks; ClassToInstanceMap positionCheck; ClassToInstanceMap rotationCheck; @@ -56,6 +58,11 @@ public class CheckManager { public ClassToInstanceMap allChecks; public CheckManager(GrimPlayer player) { + + armAnimationCheck = new ImmutableClassToInstanceMap.Builder() + .put(AutoClickerA.class, new AutoClickerA(player)) + .build(); + // Include post checks in the packet check too packetChecks = new ImmutableClassToInstanceMap.Builder() .put(Reach.class, new Reach(player)) @@ -162,6 +169,7 @@ public CheckManager(GrimPlayer player) { .build(); allChecks = new ImmutableClassToInstanceMap.Builder() + .putAll(armAnimationCheck) .putAll(packetChecks) .putAll(positionCheck) .putAll(rotationCheck) @@ -253,6 +261,12 @@ public void onPostFlyingBlockPlace(final BlockPlace place) { } } + public void onArmAnimation(final ArmAnimationUpdate armAnimationUpdate) { + for (ArmAnimationCheck check : armAnimationCheck.values()) { + check.process(armAnimationUpdate); + } + } + public ExplosionHandler getExplosionHandler() { return getPostPredictionCheck(ExplosionHandler.class); } diff --git a/src/main/java/ac/grim/grimac/utils/anticheat/update/ArmAnimationUpdate.java b/src/main/java/ac/grim/grimac/utils/anticheat/update/ArmAnimationUpdate.java new file mode 100644 index 0000000000..058acee0a6 --- /dev/null +++ b/src/main/java/ac/grim/grimac/utils/anticheat/update/ArmAnimationUpdate.java @@ -0,0 +1,15 @@ +package ac.grim.grimac.utils.anticheat.update; + +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +public class ArmAnimationUpdate { + private int leftClicks, rightClicks; + + public ArmAnimationUpdate(int leftClicks, int rightClicks) { + this.leftClicks = leftClicks; + this.rightClicks = rightClicks; + } +} diff --git a/src/main/resources/config/en.yml b/src/main/resources/config/en.yml index aff61a8f20..82f90cec8b 100644 --- a/src/main/resources/config/en.yml +++ b/src/main/resources/config/en.yml @@ -167,6 +167,11 @@ exploit: # Its valid range is limited from 2 to 4 distance-to-check-if-ghostblocks: 2 +# This is considered under AutoClicker, but it's really just a CPS detector. +# This will allow you and your staff to get alerts once that player reaches the max CPS. +AutoClicker: + max_cps: 20 + # Enable logging plugins who have injected into netty on join to debug compatibility issues debug-pipeline-on-join: false From cd911e506c5d4c02221bcdad2e09d3b7774c6ee6 Mon Sep 17 00:00:00 2001 From: "Anthony M." Date: Tue, 6 Aug 2024 21:29:45 +0300 Subject: [PATCH 5/7] fix NCE -> Grim --- src/main/java/ac/grim/grimac/manager/DiscordManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/ac/grim/grimac/manager/DiscordManager.java b/src/main/java/ac/grim/grimac/manager/DiscordManager.java index 79313355f0..3cc27f7d47 100644 --- a/src/main/java/ac/grim/grimac/manager/DiscordManager.java +++ b/src/main/java/ac/grim/grimac/manager/DiscordManager.java @@ -64,7 +64,7 @@ public void start() { private List getDefaultDescription() { List list = new ArrayList<>(); - list.add("No Cheat Enforcer Version: %nce_version%"); + list.add("Grim Version: %grim_version%"); return list; } From 4894f70263e48c7ced312b72f9a836e7f9a9b1a4 Mon Sep 17 00:00:00 2001 From: "Anthony M." Date: Thu, 8 Aug 2024 08:05:54 +0300 Subject: [PATCH 6/7] UPD: remove comment which apparently some people got confused due to it --- .../ac/grim/grimac/checks/impl/autoclicker/AutoClickerA.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/ac/grim/grimac/checks/impl/autoclicker/AutoClickerA.java b/src/main/java/ac/grim/grimac/checks/impl/autoclicker/AutoClickerA.java index a6e41e178b..04ec4588f0 100644 --- a/src/main/java/ac/grim/grimac/checks/impl/autoclicker/AutoClickerA.java +++ b/src/main/java/ac/grim/grimac/checks/impl/autoclicker/AutoClickerA.java @@ -7,9 +7,8 @@ import ac.grim.grimac.utils.anticheat.update.ArmAnimationUpdate; /* - This check is responsible for alerting everyone with grim.alerts when a player is clicking too fast. - This check is a simple example of how to use the ArmAnimationCheck interface. - You can even create your own heuristics to detect autoclickers. + Information in regard to why this was added can be found here: + https://github.com/GrimAnticheat/Grim/pull/1631 */ @CheckData(name = "AutoClickerA", alternativeName = "AutoclickerA") public class AutoClickerA extends Check implements ArmAnimationCheck { From 32fbf660298db718ea2f495dfeff44e030d7d5ce Mon Sep 17 00:00:00 2001 From: "Anthony M." Date: Sun, 11 Aug 2024 09:05:06 +0300 Subject: [PATCH 7/7] fix: grrim -> grim --- src/main/resources/discord/de.yml | 2 +- src/main/resources/discord/en.yml | 2 +- src/main/resources/discord/es.yml | 2 +- src/main/resources/discord/fr.yml | 2 +- src/main/resources/discord/it.yml | 2 +- src/main/resources/discord/pt.yml | 2 +- src/main/resources/discord/ru.yml | 2 +- src/main/resources/discord/zh.yml | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/resources/discord/de.yml b/src/main/resources/discord/de.yml index 1ebeca7a4e..e6adef0773 100644 --- a/src/main/resources/discord/de.yml +++ b/src/main/resources/discord/de.yml @@ -4,7 +4,7 @@ webhook: embed-color: "#00FFFF" show-player-head: true description: - - "Grim Version: %grrim_version%" + - "Grim Version: %grim_version%" fields: - name: "Server Information" value: diff --git a/src/main/resources/discord/en.yml b/src/main/resources/discord/en.yml index 1ebeca7a4e..e6adef0773 100644 --- a/src/main/resources/discord/en.yml +++ b/src/main/resources/discord/en.yml @@ -4,7 +4,7 @@ webhook: embed-color: "#00FFFF" show-player-head: true description: - - "Grim Version: %grrim_version%" + - "Grim Version: %grim_version%" fields: - name: "Server Information" value: diff --git a/src/main/resources/discord/es.yml b/src/main/resources/discord/es.yml index 1ebeca7a4e..e6adef0773 100644 --- a/src/main/resources/discord/es.yml +++ b/src/main/resources/discord/es.yml @@ -4,7 +4,7 @@ webhook: embed-color: "#00FFFF" show-player-head: true description: - - "Grim Version: %grrim_version%" + - "Grim Version: %grim_version%" fields: - name: "Server Information" value: diff --git a/src/main/resources/discord/fr.yml b/src/main/resources/discord/fr.yml index 1ebeca7a4e..e6adef0773 100644 --- a/src/main/resources/discord/fr.yml +++ b/src/main/resources/discord/fr.yml @@ -4,7 +4,7 @@ webhook: embed-color: "#00FFFF" show-player-head: true description: - - "Grim Version: %grrim_version%" + - "Grim Version: %grim_version%" fields: - name: "Server Information" value: diff --git a/src/main/resources/discord/it.yml b/src/main/resources/discord/it.yml index 1ebeca7a4e..e6adef0773 100644 --- a/src/main/resources/discord/it.yml +++ b/src/main/resources/discord/it.yml @@ -4,7 +4,7 @@ webhook: embed-color: "#00FFFF" show-player-head: true description: - - "Grim Version: %grrim_version%" + - "Grim Version: %grim_version%" fields: - name: "Server Information" value: diff --git a/src/main/resources/discord/pt.yml b/src/main/resources/discord/pt.yml index f29a693ab9..a0364e79d1 100644 --- a/src/main/resources/discord/pt.yml +++ b/src/main/resources/discord/pt.yml @@ -4,7 +4,7 @@ webhook: embed-color: "#00FFFF" show-player-head: true description: - - "Grim Version: %grrim_version%" + - "Grim Version: %grim_version%" fields: - name: "Server Information" value: diff --git a/src/main/resources/discord/ru.yml b/src/main/resources/discord/ru.yml index 1ebeca7a4e..e6adef0773 100644 --- a/src/main/resources/discord/ru.yml +++ b/src/main/resources/discord/ru.yml @@ -4,7 +4,7 @@ webhook: embed-color: "#00FFFF" show-player-head: true description: - - "Grim Version: %grrim_version%" + - "Grim Version: %grim_version%" fields: - name: "Server Information" value: diff --git a/src/main/resources/discord/zh.yml b/src/main/resources/discord/zh.yml index f29a693ab9..a0364e79d1 100644 --- a/src/main/resources/discord/zh.yml +++ b/src/main/resources/discord/zh.yml @@ -4,7 +4,7 @@ webhook: embed-color: "#00FFFF" show-player-head: true description: - - "Grim Version: %grrim_version%" + - "Grim Version: %grim_version%" fields: - name: "Server Information" value: