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..04ec4588f0 --- /dev/null +++ b/src/main/java/ac/grim/grimac/checks/impl/autoclicker/AutoClickerA.java @@ -0,0 +1,32 @@ +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; + +/* + 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 { + 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/manager/DiscordManager.java b/src/main/java/ac/grim/grimac/manager/DiscordManager.java index b5c734465f..3cc27f7d47 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+)?)?"); @@ -26,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; @@ -46,49 +48,140 @@ 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(); + + // 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(); } } - 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("Grim Version: %grim_version%"); 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/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 diff --git a/src/main/resources/discord/de.yml b/src/main/resources/discord/de.yml index bbbd5bbf47..e6adef0773 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: %grim_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..e6adef0773 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: %grim_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..e6adef0773 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: %grim_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..e6adef0773 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: %grim_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..e6adef0773 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: %grim_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..a0364e79d1 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: %grim_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..e6adef0773 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: %grim_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..a0364e79d1 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: %grim_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