diff --git a/build.gradle.kts b/build.gradle.kts index 62a3560865..c046c7404b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -16,7 +16,7 @@ java.targetCompatibility = JavaVersion.VERSION_17 // Set to false for debug builds // You cannot live reload classes if the jar relocates dependencies -var relocate = true; +var relocate = true repositories { mavenLocal() @@ -50,9 +50,8 @@ dependencies { implementation("org.jetbrains:annotations:24.1.0") compileOnly("org.geysermc.floodgate:api:2.0-SNAPSHOT") - compileOnly("org.spigotmc:spigot-api:1.18.2-R0.1-SNAPSHOT") + compileOnly("io.papermc.paper:paper-api:1.18.2-R0.1-SNAPSHOT") compileOnly("com.viaversion:viaversion-api:5.0.4-SNAPSHOT") - // compileOnly("io.netty:netty-all:4.1.85.Final") } @@ -158,4 +157,4 @@ tasks.shadowJar { relocate("org.intellij", "ac.grim.grimac.shaded.intellij") relocate("org.jetbrains", "ac.grim.grimac.shaded.jetbrains") } -} \ No newline at end of file +} diff --git a/src/main/java/ac/grim/grimac/checks/impl/movement/NoSlowB.java b/src/main/java/ac/grim/grimac/checks/impl/movement/NoSlowB.java index 655e540b02..69631c0497 100644 --- a/src/main/java/ac/grim/grimac/checks/impl/movement/NoSlowB.java +++ b/src/main/java/ac/grim/grimac/checks/impl/movement/NoSlowB.java @@ -35,4 +35,4 @@ public void onPacketReceive(PacketReceiveEvent event) { } } } -} \ No newline at end of file +} diff --git a/src/main/java/ac/grim/grimac/checks/impl/movement/NoSlowMitigation.java b/src/main/java/ac/grim/grimac/checks/impl/movement/NoSlowMitigation.java new file mode 100644 index 0000000000..00f1836612 --- /dev/null +++ b/src/main/java/ac/grim/grimac/checks/impl/movement/NoSlowMitigation.java @@ -0,0 +1,48 @@ +package ac.grim.grimac.checks.impl.movement; + +import ac.grim.grimac.GrimAPI; +import ac.grim.grimac.api.config.ConfigManager; +import ac.grim.grimac.checks.Check; +import ac.grim.grimac.checks.type.PostPredictionCheck; +import ac.grim.grimac.player.GrimPlayer; +import ac.grim.grimac.utils.anticheat.update.PredictionComplete; + +public class NoSlowMitigation extends Check implements PostPredictionCheck { + private double offsetToFlag; + private double bestOffset = 1; + private boolean flaggedLastTick; + + public NoSlowMitigation(GrimPlayer player) { + super(player); + } + + @Override + public void onPredictionComplete(final PredictionComplete predictionComplete) { + if (!predictionComplete.isChecked()) return; + + if (wouldFlag()) { + if (flaggedLastTick) { + player.resetBukkitItemUsage(); + } + flaggedLastTick = true; + } else flaggedLastTick = false; + + bestOffset = 1; + } + + public void handlePredictionAnalysis(double offset) { + bestOffset = Math.min(bestOffset, offset); + } + + public boolean wouldFlag() { + return player.isMitigateNoSlow() + && !player.packetStateData.isSlowedByUsingItem() + && bestOffset > offsetToFlag + && player.isUsingBukkitItem(); + } + + @Override + public void onReload(ConfigManager config) { + offsetToFlag = config.getDoubleElse("mitigate-noslow-threshold", 0.001); + } +} diff --git a/src/main/java/ac/grim/grimac/events/packets/PacketPlayerAttack.java b/src/main/java/ac/grim/grimac/events/packets/PacketPlayerAttack.java index 3a779984ad..34253790ad 100644 --- a/src/main/java/ac/grim/grimac/events/packets/PacketPlayerAttack.java +++ b/src/main/java/ac/grim/grimac/events/packets/PacketPlayerAttack.java @@ -43,6 +43,10 @@ public void onPacketReceive(PacketReceiveEvent event) { } if (interact.getAction() == WrapperPlayClientInteractEntity.InteractAction.ATTACK) { + if (player.isMitigateAutoBlock()) { + player.resetBukkitItemUsage(); + } + ItemStack heldItem = player.getInventory().getHeldItem(); PacketEntity entity = player.compensatedEntities.getEntity(interact.getEntityId()); diff --git a/src/main/java/ac/grim/grimac/manager/CheckManager.java b/src/main/java/ac/grim/grimac/manager/CheckManager.java index d5e3caafac..e37187d053 100644 --- a/src/main/java/ac/grim/grimac/manager/CheckManager.java +++ b/src/main/java/ac/grim/grimac/manager/CheckManager.java @@ -129,6 +129,7 @@ public CheckManager(GrimPlayer player) { .put(SuperDebug.class, new SuperDebug(player)) .put(DebugHandler.class, new DebugHandler(player)) .put(EntityControl.class, new EntityControl(player)) + .put(NoSlowMitigation.class, new NoSlowMitigation(player)) .put(NoSlowA.class, new NoSlowA(player)) .put(NoSlowC.class, new NoSlowC(player)) .put(NoSlowD.class, new NoSlowD(player)) diff --git a/src/main/java/ac/grim/grimac/player/GrimPlayer.java b/src/main/java/ac/grim/grimac/player/GrimPlayer.java index 250eb2dbf6..e247c71fa3 100644 --- a/src/main/java/ac/grim/grimac/player/GrimPlayer.java +++ b/src/main/java/ac/grim/grimac/player/GrimPlayer.java @@ -53,22 +53,31 @@ import net.kyori.adventure.text.TranslatableComponent; import org.bukkit.Bukkit; import org.bukkit.ChatColor; +import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.util.Vector; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; +import java.util.function.Predicate; // Everything in this class should be sync'd to the anticheat thread. // Put variables sync'd to the netty thread in PacketStateData // Variables that need lag compensation should have their own class // Soon there will be a generic class for lag compensation public class GrimPlayer implements GrimUser { + public static final @Nullable Consumer<@NotNull Player> resetActiveBukkitItem; + public static final @Nullable Predicate<@NotNull Player> isUsingBukkitItem; + public UUID playerUUID; public final User user; public int entityID; @@ -725,6 +734,8 @@ public void runNettyTaskInMs(Runnable runnable, int ms) { @Getter private boolean ignoreDuplicatePacketRotation = false; @Getter private boolean experimentalChecks = false; @Getter private boolean cancelDuplicatePacket = true; + @Getter private boolean mitigateAutoBlock = true; + @Getter private boolean mitigateNoSlow = true; @Override public void reload(ConfigManager config) { @@ -733,6 +744,8 @@ public void reload(ConfigManager config) { experimentalChecks = config.getBooleanElse("experimental-checks", false); ignoreDuplicatePacketRotation = config.getBooleanElse("ignore-duplicate-packet-rotation", false); cancelDuplicatePacket = config.getBooleanElse("cancel-duplicate-packet", true); + mitigateAutoBlock = config.getBooleanElse("mitigate-auto-block", true); + mitigateNoSlow = config.getBooleanElse("mitigate-noslow", true); // reload all checks for (AbstractCheck value : checkManager.allChecks.values()) value.reload(config); // reload punishment manager @@ -743,4 +756,112 @@ public void reload(ConfigManager config) { public void reload() { reload(GrimAPI.INSTANCE.getConfigManager().getConfig()); } + + public void resetBukkitItemUsage() { + if (resetActiveBukkitItem != null && this.isUsingBukkitItem()) { + this.bukkitPlayer.updateInventory(); + resetActiveBukkitItem.accept(this.bukkitPlayer); + } + } + + public boolean isUsingBukkitItem() { + return isUsingBukkitItem != null && this.bukkitPlayer != null && isUsingBukkitItem.test(this.bukkitPlayer); + } + + static { + ServerVersion version = PacketEvents.getAPI().getServerManager().getVersion(); + Predicate isUsingBukkitItem0 = null; + Consumer resetActiveBukkitItem0 = null; + + try { + try { // paper 1.16+ + LivingEntity.class.getMethod("clearActiveItem"); + resetActiveBukkitItem0 = LivingEntity::clearActiveItem; + } catch (NoSuchMethodException ignored) {} + + if (version == ServerVersion.V_1_8_8) { + Class EntityHuman = Class.forName("net.minecraft.server.v1_8_R3.EntityHuman"); + Method getHandle = Class.forName("org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer").getMethod("getHandle"); + Method clearActiveItem = EntityHuman.getMethod("bV"); + Method isUsingItem = EntityHuman.getMethod("bS"); + + resetActiveBukkitItem0 = player -> { + try { + clearActiveItem.invoke(getHandle.invoke(player)); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + }; + + isUsingBukkitItem0 = player -> { + try { + return (boolean) isUsingItem.invoke(getHandle.invoke(player)); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + }; + } else if (version == ServerVersion.V_1_12_2) { + Class EntityLiving = Class.forName("net.minecraft.server.v1_12_R1.EntityLiving"); + Method getHandle = Class.forName("org.bukkit.craftbukkit.v1_12_R1.entity.CraftPlayer").getMethod("getHandle"); + Method clearActiveItem = EntityLiving.getMethod("cN"); + Method getItemInUse = EntityLiving.getMethod("cJ"); + Method isEmpty = Class.forName("net.minecraft.server.v1_12_R1.ItemStack").getMethod("isEmpty"); + + resetActiveBukkitItem0 = player -> { + try { + clearActiveItem.invoke(getHandle.invoke(player)); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + }; + + isUsingBukkitItem0 = player -> { + try { + var item = getItemInUse.invoke(getHandle.invoke(player)); + return item != null && !((boolean) isEmpty.invoke(item)); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + }; + } else if (version == ServerVersion.V_1_16_5) { + Class EntityLiving = Class.forName("net.minecraft.server.v1_16_R3.EntityLiving"); + Method getHandle = Class.forName("org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer").getMethod("getHandle"); + Method clearActiveItem = EntityLiving.getMethod("clearActiveItem"); + Method getItemInUse = EntityLiving.getMethod("getActiveItem"); + Method isEmpty = Class.forName("net.minecraft.server.v1_16_R3.ItemStack").getMethod("isEmpty"); + + resetActiveBukkitItem0 = player -> { + try { + clearActiveItem.invoke(getHandle.invoke(player)); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + }; + + isUsingBukkitItem0 = player -> { + try { + Object item = getItemInUse.invoke(getHandle.invoke(player)); + return item != null && !((boolean) isEmpty.invoke(item)); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + }; + } else if (version.isNewerThanOrEquals(ServerVersion.V_1_17_1)) { + isUsingBukkitItem0 = player -> player.getItemInUse() != null; + } + } catch (ClassNotFoundException | NoSuchMethodException e) { + throw new RuntimeException(e); + } finally { + resetActiveBukkitItem = resetActiveBukkitItem0; + isUsingBukkitItem = isUsingBukkitItem0; + + if (resetActiveBukkitItem == null) { + LogUtil.warn("could not find method to reset item usage (are you using spigot?)"); + } + + if (isUsingBukkitItem == null) { + LogUtil.warn("could not find method to get item usage status (are you using an unsupported version?)"); + } + } + } } diff --git a/src/main/java/ac/grim/grimac/predictionengine/predictions/PredictionEngine.java b/src/main/java/ac/grim/grimac/predictionengine/predictions/PredictionEngine.java index 0d75b8e69f..6298e02413 100644 --- a/src/main/java/ac/grim/grimac/predictionengine/predictions/PredictionEngine.java +++ b/src/main/java/ac/grim/grimac/predictionengine/predictions/PredictionEngine.java @@ -1,5 +1,6 @@ package ac.grim.grimac.predictionengine.predictions; +import ac.grim.grimac.checks.impl.movement.NoSlowMitigation; import ac.grim.grimac.player.GrimPlayer; import ac.grim.grimac.predictionengine.SneakingEstimator; import ac.grim.grimac.predictionengine.movementtick.MovementTickerPlayer; @@ -174,6 +175,10 @@ private void doPredictions(GrimPlayer player, List possibleVelocitie player.checkManager.getNoSlow().handlePredictionAnalysis(Math.sqrt(player.uncertaintyHandler.reduceOffset(resultAccuracy))); } + if (!player.packetStateData.isSlowedByUsingItem() && clientVelAfterInput.isFlipItem()) { + player.checkManager.getPostPredictionCheck(NoSlowMitigation.class).handlePredictionAnalysis(Math.sqrt(player.uncertaintyHandler.reduceOffset(resultAccuracy))); + } + if (player.checkManager.getKnockbackHandler().shouldIgnoreForPrediction(clientVelAfterInput) || player.checkManager.getExplosionHandler().shouldIgnoreForPrediction(clientVelAfterInput)) { continue; @@ -192,7 +197,7 @@ private void doPredictions(GrimPlayer player, List possibleVelocitie } // Close enough, there's no reason to continue our predictions (if either kb or explosion will flag, continue searching) - if (bestInput < 1e-5 * 1e-5 && !player.checkManager.getKnockbackHandler().wouldFlag() && !player.checkManager.getExplosionHandler().wouldFlag()) { + if (bestInput < 1e-5 * 1e-5 && !player.checkManager.getKnockbackHandler().wouldFlag() && !player.checkManager.getExplosionHandler().wouldFlag() && !player.checkManager.getPostPredictionCheck(NoSlowMitigation.class).wouldFlag()) { break; } } diff --git a/src/main/resources/config/de.yml b/src/main/resources/config/de.yml index 29d10af784..0f77db4ac8 100644 --- a/src/main/resources/config/de.yml +++ b/src/main/resources/config/de.yml @@ -170,6 +170,11 @@ debug-pipeline-on-join: false # Aktiviert experimentelle Prüfungen experimental-checks: false +mitigate-auto-block: true + +mitigate-noslow: true +mitigate-noslow-threshold: 0.001 + # Grim bricht manchmal illegale Pakete ab, z.B. mit Timer, nachdem X Pakete in einer Sekunde abgebrochen wurden, wann sollte # wir den Spieler einfach kicken? Dies ist erforderlich, da einige Paketbegrenzer die von Grim abgebrochenen Pakete nicht zählen. packet-spam-threshold: 100 diff --git a/src/main/resources/config/en.yml b/src/main/resources/config/en.yml index aff61a8f20..15390f111b 100644 --- a/src/main/resources/config/en.yml +++ b/src/main/resources/config/en.yml @@ -173,6 +173,11 @@ debug-pipeline-on-join: false # Enables experimental checks experimental-checks: false +mitigate-auto-block: true + +mitigate-noslow: true +mitigate-noslow-threshold: 0.001 + # Grim sometimes cancels illegal packets such as with timer, after X packets in a second cancelled, when should # we simply kick the player? This is required as some packet limiters don't count packets cancelled by grim. packet-spam-threshold: 100 diff --git a/src/main/resources/config/es.yml b/src/main/resources/config/es.yml index b83737e466..ce9562fd36 100644 --- a/src/main/resources/config/es.yml +++ b/src/main/resources/config/es.yml @@ -174,6 +174,11 @@ debug-pipeline-on-join: false # Habilitar comprobaciones experimentales experimental-checks: false +mitigate-auto-block: true + +mitigate-noslow: true +mitigate-noslow-threshold: 0.001 + # Grim a veces cancela paquetes ilegal como los de Timer. Después de X paquetes en un solo segundo cancelados, # cuando deberíamos simplemente expulsar al jugador? Esto es obligatorio ya que algunos limitadores de paquetes # no cuentan los paquetes cancelados por Grim. diff --git a/src/main/resources/config/fr.yml b/src/main/resources/config/fr.yml index 0e1c36c451..c7f29a92fd 100644 --- a/src/main/resources/config/fr.yml +++ b/src/main/resources/config/fr.yml @@ -169,6 +169,11 @@ debug-pipeline-on-join: false # Active les vérifications expérimentales experimental-checks: false +mitigate-auto-block: true + +mitigate-noslow: true +mitigate-noslow-threshold: 0.001 + # Grim annule parfois des paquets illégaux, comme ceux liés au chronomètre, après avoir annulé X paquets en une seconde, # à partir de combien de paquets annulés devrions-nous simplement expulser le joueur ? # Cela est nécessaire car certains limiteurs de paquets ne comptent pas les paquets annulés par Grim. diff --git a/src/main/resources/config/it.yml b/src/main/resources/config/it.yml index 82926afb77..6550a69e11 100644 --- a/src/main/resources/config/it.yml +++ b/src/main/resources/config/it.yml @@ -150,6 +150,11 @@ debug-pipeline-on-join: false # Enables experimental checks experimental-checks: false +mitigate-auto-block: true + +mitigate-noslow: true +mitigate-noslow-threshold: 0.001 + # Grim sometimes cancels illegal packets such as with timer, after X packets in a second cancelled, when should # we simply kick the player? This is required as some packet limiters don't count packets cancelled by grim. packet-spam-threshold: 100 diff --git a/src/main/resources/config/nl.yml b/src/main/resources/config/nl.yml index f198782f54..b158547878 100644 --- a/src/main/resources/config/nl.yml +++ b/src/main/resources/config/nl.yml @@ -173,6 +173,11 @@ debug-pipeline-on-join: false # Experimentele controles inschakelen experimental-checks: false +mitigate-auto-block: true + +mitigate-noslow: true +mitigate-noslow-threshold: 0.001 + # Grim annuleert soms illegale pakketten zoals met timer, na X pakketten in een seconde geannuleerd, wanneer moeten # we de speler gewoon schoppen? Dit is nodig omdat sommige pakket-begrenzers pakketten die door grim worden geannuleerd niet tellen packet-spam-threshold: 100 diff --git a/src/main/resources/config/pt.yml b/src/main/resources/config/pt.yml index 386bf6d103..4d21d9e61f 100644 --- a/src/main/resources/config/pt.yml +++ b/src/main/resources/config/pt.yml @@ -178,6 +178,11 @@ debug-pipeline-on-join: false # Habilita verificações experimentais. experimental-checks: false +mitigate-auto-block: true + +mitigate-noslow: true +mitigate-noslow-threshold: 0.001 + # Grim às vezes cancela pacotes ilegais como com o timer, depois de X pacotes em um segundo que foram cancelados, quando # deve-se simplesmente expulsar o jogador? Isso é requirido já que alguns limitadores de pacotes não contam pacotes # cancelados pelo Grim. diff --git a/src/main/resources/config/ru.yml b/src/main/resources/config/ru.yml index 0cd1bae724..2bceebb9ea 100644 --- a/src/main/resources/config/ru.yml +++ b/src/main/resources/config/ru.yml @@ -112,7 +112,6 @@ Knockback: # Это сделано для того, чтобы игрок не собирал слишком много нарушений и никогда не смог очистить их все. max-ceiling: 4 - Explosion: threshold: 0.001 setbackvl: 3 @@ -169,6 +168,11 @@ debug-pipeline-on-join: false # Включает экспериментальные проверки experimental-checks: false +mitigate-auto-block: true + +mitigate-noslow: true +mitigate-noslow-threshold: 0.001 + # Грим иногда отменяет незаконные пакеты, например, с таймером, после X пакетов в секунду отмененных, когда следует # нам просто кикнуть игрока? Это необходимо, так как некоторые ограничители пакетов не учитывают пакеты, отмененные Гримом. packet-spam-threshold: 100 diff --git a/src/main/resources/config/tr.yml b/src/main/resources/config/tr.yml index 4e5e23c480..1cbf7055ac 100644 --- a/src/main/resources/config/tr.yml +++ b/src/main/resources/config/tr.yml @@ -173,6 +173,11 @@ debug-pipeline-on-join: false # Deneysel kontrolleri etkinleştir experimental-checks: false +mitigate-auto-block: true + +mitigate-noslow: true +mitigate-noslow-threshold: 0.001 + # Grim bazen timer gibi yasa dışı paketleri iptal eder; bir saniyede X paket iptal edildikten sonra, ne zaman # oyuncuyu basitçe atmalıyız? Bu, bazı paket sınırlayıcılarının Grim tarafından iptal edilen paketleri saymaması gerektiğindendir. packet-spam-threshold: 100 diff --git a/src/main/resources/config/zh.yml b/src/main/resources/config/zh.yml index 01cacd08eb..c010d1f8a5 100644 --- a/src/main/resources/config/zh.yml +++ b/src/main/resources/config/zh.yml @@ -171,6 +171,11 @@ debug-pipeline-on-join: false # 启用实验性检查 experimental-checks: false +mitigate-auto-block: true + +mitigate-noslow: true +mitigate-noslow-threshold: 0.001 + # Grim有时会取消非法的数据包,比如用timer,在一秒钟内取消了数个数据包后,我们应该踢掉这个玩家? # 我们认为是应该的,因为有些数据包限制器并不计算被Grim取消的数据包。 packet-spam-threshold: 100